diff --git a/app/browsemainpage.h b/app/browsemainpage.h index f2b70a5f..05bb5423 100644 --- a/app/browsemainpage.h +++ b/app/browsemainpage.h @@ -1,93 +1,93 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 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, Cambridge, MA 02110-1301, USA. */ #ifndef BROWSEMAINPAGE_H #define BROWSEMAINPAGE_H // Qt #include // KDE #include // Local class QDropEvent; class QModelIndex; class QItemSelection; class QToolButton; class KActionCollection; class KUrlNavigator; namespace Gwenview { class GvCore; class ThumbnailView; struct BrowseMainPagePrivate; /** * This class contains all the necessary widgets displayed in browse mode: * the thumbnail view, the url navigator, the bottom bar. */ class BrowseMainPage : public QWidget { Q_OBJECT public: BrowseMainPage(QWidget* parent, KActionCollection*, GvCore*); - ~BrowseMainPage() Q_DECL_OVERRIDE; + ~BrowseMainPage() override; void reload(); ThumbnailView* thumbnailView() const; KUrlNavigator* urlNavigator() const; void loadConfig(); void saveConfig() const; void setFullScreenMode(bool); void setStatusBarVisible(bool); QToolButton* toggleSideBarButton() const; private Q_SLOTS: void editLocation(); void addFolderToPlaces(); void slotDirModelRowsInserted(const QModelIndex& parent, int start, int end); void slotDirModelRowsAboutToBeRemoved(const QModelIndex& parent, int start, int end); void slotDirModelReset(); void slotSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected); void updateSortOrder(); void updateThumbnailDetails(); void slotUrlsDropped(const QUrl &destUrl, QDropEvent*); void showMenuForDroppedUrls(const QList&, const QUrl &destUrl); protected: - bool eventFilter(QObject* watched, QEvent* event) Q_DECL_OVERRIDE; + bool eventFilter(QObject* watched, QEvent* event) override; private: BrowseMainPagePrivate* const d; }; } // namespace #endif /* BROWSEMAINPAGE_H */ diff --git a/app/documentinfoprovider.h b/app/documentinfoprovider.h index 4a622051..c17c2a24 100644 --- a/app/documentinfoprovider.h +++ b/app/documentinfoprovider.h @@ -1,54 +1,54 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2010 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, Cambridge, MA 02110-1301, USA. */ #ifndef DOCUMENTINFOPROVIDER_H #define DOCUMENTINFOPROVIDER_H // Qt // KDE // Local #include namespace Gwenview { class SortedDirModel; class DocumentInfoProvider : public AbstractDocumentInfoProvider { Q_OBJECT public: DocumentInfoProvider(SortedDirModel* model); - bool isBusy(const QUrl &url) Q_DECL_OVERRIDE; + bool isBusy(const QUrl &url) override; - bool isModified(const QUrl &url) Q_DECL_OVERRIDE; + bool isModified(const QUrl &url) override; - void thumbnailForDocument(const QUrl &url, ThumbnailGroup::Enum group, QPixmap* outPix, QSize* outFullSize) const Q_DECL_OVERRIDE; + void thumbnailForDocument(const QUrl &url, ThumbnailGroup::Enum group, QPixmap* outPix, QSize* outFullSize) const override; private: SortedDirModel* mDirModel; }; } // namespace #endif /* DOCUMENTINFOPROVIDER_H */ diff --git a/app/filtercontroller.cpp b/app/filtercontroller.cpp index 361b4f1e..c3bfb47c 100644 --- a/app/filtercontroller.cpp +++ b/app/filtercontroller.cpp @@ -1,359 +1,359 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 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, Cambridge, MA 02110-1301, USA. */ // Self #include "filtercontroller.h" #include // Qt #include #include #include #include #include #include #include #include #include // KDE #include #include #include #include // Local #include #include #include #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE // KDE #include // Local #include #include #endif namespace Gwenview { NameFilterWidget::NameFilterWidget(SortedDirModel* model) { mFilter = new NameFilter(model); mModeComboBox = new KComboBox; mModeComboBox->addItem(i18n("Name contains"), QVariant(NameFilter::Contains)); mModeComboBox->addItem(i18n("Name does not contain"), QVariant(NameFilter::DoesNotContain)); mLineEdit = new QLineEdit; QHBoxLayout* layout = new QHBoxLayout(this); layout->setMargin(0); layout->setSpacing(2); layout->addWidget(mModeComboBox); layout->addWidget(mLineEdit); QTimer* timer = new QTimer(this); timer->setInterval(350); timer->setSingleShot(true); connect(timer, SIGNAL(timeout()), SLOT(applyNameFilter())); connect(mLineEdit, SIGNAL(textChanged(QString)), timer, SLOT(start())); connect(mModeComboBox, SIGNAL(currentIndexChanged(int)), SLOT(applyNameFilter())); QTimer::singleShot(0, mLineEdit, SLOT(setFocus())); } NameFilterWidget::~NameFilterWidget() { delete mFilter; } void NameFilterWidget::applyNameFilter() { QVariant data = mModeComboBox->itemData(mModeComboBox->currentIndex()); mFilter->setMode(NameFilter::Mode(data.toInt())); mFilter->setText(mLineEdit->text()); } DateFilterWidget::DateFilterWidget(SortedDirModel* model) { mFilter = new DateFilter(model); mModeComboBox = new KComboBox; mModeComboBox->addItem(i18n("Date >="), DateFilter::GreaterOrEqual); mModeComboBox->addItem(i18n("Date ="), DateFilter::Equal); mModeComboBox->addItem(i18n("Date <="), DateFilter::LessOrEqual); mDateWidget = new DateWidget; QHBoxLayout* layout = new QHBoxLayout(this); layout->setMargin(0); layout->addWidget(mModeComboBox); layout->addWidget(mDateWidget); connect(mDateWidget, SIGNAL(dateChanged(QDate)), SLOT(applyDateFilter())); connect(mModeComboBox, SIGNAL(currentIndexChanged(int)), SLOT(applyDateFilter())); applyDateFilter(); } DateFilterWidget::~DateFilterWidget() { delete mFilter; } void DateFilterWidget::applyDateFilter() { QVariant data = mModeComboBox->itemData(mModeComboBox->currentIndex()); mFilter->setMode(DateFilter::Mode(data.toInt())); mFilter->setDate(mDateWidget->date()); } #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE RatingFilterWidget::RatingFilterWidget(SortedDirModel* model) { mModeComboBox = new KComboBox; mModeComboBox->addItem(i18n("Rating >="), RatingFilter::GreaterOrEqual); mModeComboBox->addItem(i18n("Rating =") , RatingFilter::Equal); mModeComboBox->addItem(i18n("Rating <="), RatingFilter::LessOrEqual); mRatingWidget = new KRatingWidget; mRatingWidget->setHalfStepsEnabled(true); mRatingWidget->setMaxRating(10); QHBoxLayout* layout = new QHBoxLayout(this); layout->setMargin(0); layout->addWidget(mModeComboBox); layout->addWidget(mRatingWidget); mFilter = new RatingFilter(model); QObject::connect(mModeComboBox, SIGNAL(currentIndexChanged(int)), SLOT(updateFilterMode())); QObject::connect(mRatingWidget, SIGNAL(ratingChanged(int)), SLOT(slotRatingChanged(int))); updateFilterMode(); } RatingFilterWidget::~RatingFilterWidget() { delete mFilter; } void RatingFilterWidget::slotRatingChanged(int value) { mFilter->setRating(value); } void RatingFilterWidget::updateFilterMode() { QVariant data = mModeComboBox->itemData(mModeComboBox->currentIndex()); mFilter->setMode(RatingFilter::Mode(data.toInt())); } TagFilterWidget::TagFilterWidget(SortedDirModel* model) { mFilter = new TagFilter(model); mModeComboBox = new KComboBox; mModeComboBox->addItem(i18n("Tagged"), QVariant(true)); mModeComboBox->addItem(i18n("Not Tagged"), QVariant(false)); mTagComboBox = new QComboBox; QHBoxLayout* layout = new QHBoxLayout(this); layout->setMargin(0); layout->addWidget(mModeComboBox); layout->addWidget(mTagComboBox); AbstractSemanticInfoBackEnd* backEnd = model->semanticInfoBackEnd(); backEnd->refreshAllTags(); TagModel* tagModel = TagModel::createAllTagsModel(this, backEnd); QCompleter* completer = new QCompleter(mTagComboBox); completer->setCaseSensitivity(Qt::CaseInsensitive); completer->setModel(tagModel); mTagComboBox->setCompleter(completer); mTagComboBox->setInsertPolicy(QComboBox::NoInsert); mTagComboBox->setEditable(true); mTagComboBox->setModel(tagModel); mTagComboBox->setCurrentIndex(-1); connect(mTagComboBox, SIGNAL(currentIndexChanged(int)), SLOT(updateTagSetFilter())); connect(mModeComboBox, SIGNAL(currentIndexChanged(int)), SLOT(updateTagSetFilter())); QTimer::singleShot(0, mTagComboBox, SLOT(setFocus())); } TagFilterWidget::~TagFilterWidget() { delete mFilter; } void TagFilterWidget::updateTagSetFilter() { QModelIndex index = mTagComboBox->model()->index(mTagComboBox->currentIndex(), 0); if (!index.isValid()) { qWarning() << "Invalid index"; return; } SemanticInfoTag tag = index.data(TagModel::TagRole).toString(); mFilter->setTag(tag); bool wantMatchingTag = mModeComboBox->itemData(mModeComboBox->currentIndex()).toBool(); mFilter->setWantMatchingTag(wantMatchingTag); } #endif /** * A container for all filter widgets. It features a close button on the right. */ class FilterWidgetContainer : public QFrame { public: FilterWidgetContainer() { QPalette pal = palette(); pal.setColor(QPalette::Window, pal.color(QPalette::Highlight)); setPalette(pal); } void setFilterWidget(QWidget* widget) { QToolButton* closeButton = new QToolButton; closeButton->setIcon(QIcon::fromTheme("window-close")); closeButton->setAutoRaise(true); closeButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum); int size = IconSize(KIconLoader::Small); closeButton->setIconSize(QSize(size, size)); connect(closeButton, SIGNAL(clicked()), SLOT(deleteLater())); QHBoxLayout* layout = new QHBoxLayout(this); layout->setMargin(2); layout->setSpacing(2); layout->addWidget(widget); layout->addWidget(closeButton); } protected: - void paintEvent(QPaintEvent*) Q_DECL_OVERRIDE + void paintEvent(QPaintEvent*) override { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); QPainterPath path = PaintUtils::roundedRectangle(QRectF(rect()).adjusted(0.5, 0.5, -0.5, -0.5), 6); QColor color = palette().color(QPalette::Highlight); painter.fillPath(path, PaintUtils::alphaAdjustedF(color, 0.5)); painter.setPen(color); painter.drawPath(path); } }; FilterController::FilterController(QFrame* frame, SortedDirModel* dirModel) : QObject(frame) { q = this; mFrame = frame; mDirModel = dirModel; mFilterWidgetCount = 0; mFrame->hide(); FlowLayout* layout = new FlowLayout(mFrame); layout->setSpacing(2); addAction(i18nc("@action:inmenu", "Filter by Name"), SLOT(addFilterByName())); addAction(i18nc("@action:inmenu", "Filter by Date"), SLOT(addFilterByDate())); #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE addAction(i18nc("@action:inmenu", "Filter by Rating"), SLOT(addFilterByRating())); addAction(i18nc("@action:inmenu", "Filter by Tag"), SLOT(addFilterByTag())); #endif } QList FilterController::actionList() const { return mActionList; } void FilterController::addFilterByName() { addFilter(new NameFilterWidget(mDirModel)); } void FilterController::addFilterByDate() { addFilter(new DateFilterWidget(mDirModel)); } #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE void FilterController::addFilterByRating() { addFilter(new RatingFilterWidget(mDirModel)); } void FilterController::addFilterByTag() { addFilter(new TagFilterWidget(mDirModel)); } #endif void FilterController::slotFilterWidgetClosed() { mFilterWidgetCount--; if (mFilterWidgetCount == 0) { mFrame->hide(); } } void FilterController::addAction(const QString& text, const char* slot) { QAction* action = new QAction(text, q); QObject::connect(action, SIGNAL(triggered()), q, slot); mActionList << action; } void FilterController::addFilter(QWidget* widget) { if (mFrame->isHidden()) { mFrame->show(); } FilterWidgetContainer* container = new FilterWidgetContainer; container->setFilterWidget(widget); mFrame->layout()->addWidget(container); mFilterWidgetCount++; QObject::connect(container, SIGNAL(destroyed()), q, SLOT(slotFilterWidgetClosed())); } } // namespace diff --git a/app/filtercontroller.h b/app/filtercontroller.h index 78ff82c2..97ce01ee 100644 --- a/app/filtercontroller.h +++ b/app/filtercontroller.h @@ -1,369 +1,369 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 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, Cambridge, MA 02110-1301, USA. */ #ifndef FILTERCONTROLLER_H #define FILTERCONTROLLER_H #include // Qt #include #include #include #include // KDE #include // Local #include #include #include #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE // KDE #include // Local #include #include #endif class QAction; class QFrame; class QLineEdit; class QComboBox; class KComboBox; namespace Gwenview { class SortedDirModel; /** * An AbstractSortedDirModelFilter which filters on the file names */ class NameFilter : public AbstractSortedDirModelFilter { public: enum Mode { Contains, DoesNotContain }; NameFilter(SortedDirModel* model) : AbstractSortedDirModelFilter(model) , mText() , mMode(Contains) {} - bool needsSemanticInfo() const Q_DECL_OVERRIDE + bool needsSemanticInfo() const override { return false; } - bool acceptsIndex(const QModelIndex& index) const Q_DECL_OVERRIDE + bool acceptsIndex(const QModelIndex& index) const override { if (mText.isEmpty()) { return true; } switch (mMode) { case Contains: return index.data().toString().contains(mText, Qt::CaseInsensitive); default: /*DoesNotContain:*/ return !index.data().toString().contains(mText, Qt::CaseInsensitive); } } void setText(const QString& text) { mText = text; model()->applyFilters(); } void setMode(Mode mode) { mMode = mode; model()->applyFilters(); } private: QString mText; Mode mMode; }; class NameFilterWidget : public QWidget { Q_OBJECT public: NameFilterWidget(SortedDirModel*); ~NameFilterWidget(); private Q_SLOTS: void applyNameFilter(); private: QPointer mFilter; KComboBox* mModeComboBox; QLineEdit* mLineEdit; }; /** * An AbstractSortedDirModelFilter which filters on the file dates */ class DateFilter : public AbstractSortedDirModelFilter { public: enum Mode { GreaterOrEqual, Equal, LessOrEqual }; DateFilter(SortedDirModel* model) : AbstractSortedDirModelFilter(model) , mMode(GreaterOrEqual) {} - bool needsSemanticInfo() const Q_DECL_OVERRIDE + bool needsSemanticInfo() const override { return false; } - bool acceptsIndex(const QModelIndex& index) const Q_DECL_OVERRIDE + bool acceptsIndex(const QModelIndex& index) const override { if (!mDate.isValid()) { return true; } KFileItem fileItem = model()->itemForSourceIndex(index); QDate date = TimeUtils::dateTimeForFileItem(fileItem).date(); switch (mMode) { case GreaterOrEqual: return date >= mDate; case Equal: return date == mDate; default: /* LessOrEqual */ return date <= mDate; } } void setDate(const QDate& date) { mDate = date; model()->applyFilters(); } void setMode(Mode mode) { mMode = mode; model()->applyFilters(); } private: QDate mDate; Mode mMode; }; class DateFilterWidget : public QWidget { Q_OBJECT public: DateFilterWidget(SortedDirModel*); ~DateFilterWidget(); private Q_SLOTS: void applyDateFilter(); private: QPointer mFilter; KComboBox* mModeComboBox; DateWidget* mDateWidget; }; #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE /** * An AbstractSortedDirModelFilter which filters on file ratings */ class RatingFilter : public AbstractSortedDirModelFilter { public: enum Mode { GreaterOrEqual, Equal, LessOrEqual }; RatingFilter(SortedDirModel* model) : AbstractSortedDirModelFilter(model) , mRating(0) , mMode(GreaterOrEqual) {} - bool needsSemanticInfo() const Q_DECL_OVERRIDE + bool needsSemanticInfo() const override { return true; } - bool acceptsIndex(const QModelIndex& index) const Q_DECL_OVERRIDE + bool acceptsIndex(const QModelIndex& index) const override { SemanticInfo info = model()->semanticInfoForSourceIndex(index); switch (mMode) { case GreaterOrEqual: return info.mRating >= mRating; case Equal: return info.mRating == mRating; default: /* LessOrEqual */ return info.mRating <= mRating; } } void setRating(int value) { mRating = value; model()->applyFilters(); } void setMode(Mode mode) { mMode = mode; model()->applyFilters(); } private: int mRating; Mode mMode; }; class RatingFilterWidget : public QWidget { Q_OBJECT public: RatingFilterWidget(SortedDirModel*); ~RatingFilterWidget(); private Q_SLOTS: void slotRatingChanged(int value); void updateFilterMode(); private: KComboBox* mModeComboBox; KRatingWidget* mRatingWidget; QPointer mFilter; }; /** * An AbstractSortedDirModelFilter which filters on associated tags */ class TagFilter : public AbstractSortedDirModelFilter { public: TagFilter(SortedDirModel* model) : AbstractSortedDirModelFilter(model) , mWantMatchingTag(true) {} - bool needsSemanticInfo() const Q_DECL_OVERRIDE + bool needsSemanticInfo() const override { return true; } - bool acceptsIndex(const QModelIndex& index) const Q_DECL_OVERRIDE + bool acceptsIndex(const QModelIndex& index) const override { if (mTag.isEmpty()) { return true; } SemanticInfo info = model()->semanticInfoForSourceIndex(index); if (mWantMatchingTag) { return info.mTags.contains(mTag); } else { return !info.mTags.contains(mTag); } } void setTag(const SemanticInfoTag& tag) { mTag = tag; model()->applyFilters(); } void setWantMatchingTag(bool value) { mWantMatchingTag = value; model()->applyFilters(); } private: SemanticInfoTag mTag; bool mWantMatchingTag; }; class TagFilterWidget : public QWidget { Q_OBJECT public: TagFilterWidget(SortedDirModel*); ~TagFilterWidget(); private Q_SLOTS: void updateTagSetFilter(); private: KComboBox* mModeComboBox; QComboBox* mTagComboBox; QPointer mFilter; }; #endif /** * This class manages the filter widgets in the filter frame and assign the * corresponding filters to the SortedDirModel */ class FilterController : public QObject { Q_OBJECT public: FilterController(QFrame* filterFrame, SortedDirModel* model); QList actionList() const; private Q_SLOTS: void addFilterByName(); void addFilterByDate(); #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE void addFilterByRating(); void addFilterByTag(); #endif void slotFilterWidgetClosed(); private: void addAction(const QString& text, const char* slot); void addFilter(QWidget* widget); FilterController* q; QFrame* mFrame; SortedDirModel* mDirModel; QList mActionList; int mFilterWidgetCount; /**< How many filter widgets are in mFrame */ }; } // namespace #endif /* FILTERCONTROLLER_H */ diff --git a/app/folderviewcontextmanageritem.cpp b/app/folderviewcontextmanageritem.cpp index 5f0a6c3a..e4e46837 100644 --- a/app/folderviewcontextmanageritem.cpp +++ b/app/folderviewcontextmanageritem.cpp @@ -1,273 +1,273 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2009 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 "folderviewcontextmanageritem.h" // Qt #include #include #include #include #include #include // KDE #include // Local #include #include #include "sidebar.h" #include "fileoperations.h" namespace Gwenview { /** * This treeview accepts url drops */ class UrlDropTreeView : public QTreeView { public: explicit UrlDropTreeView(QWidget* parent = nullptr) : QTreeView(parent) {} protected: - void dragEnterEvent(QDragEnterEvent* event) Q_DECL_OVERRIDE + void dragEnterEvent(QDragEnterEvent* event) override { QAbstractItemView::dragEnterEvent(event); setDirtyRegion(mDropRect); if (event->mimeData()->hasUrls()) { event->acceptProposedAction(); } } - void dragMoveEvent(QDragMoveEvent* event) Q_DECL_OVERRIDE + void dragMoveEvent(QDragMoveEvent* event) override { QAbstractItemView::dragMoveEvent(event); QModelIndex index = indexAt(event->pos()); // This code has been copied from Dolphin // (panels/folders/paneltreeview.cpp) setDirtyRegion(mDropRect); mDropRect = visualRect(index); setDirtyRegion(mDropRect); if (index.isValid()) { event->acceptProposedAction(); } else { event->ignore(); } } - void dropEvent(QDropEvent* event) Q_DECL_OVERRIDE + void dropEvent(QDropEvent* event) override { const QList urlList = KUrlMimeData::urlsFromMimeData(event->mimeData()); const QModelIndex index = indexAt(event->pos()); if (!index.isValid()) { qWarning() << "Invalid index!"; return; } const QUrl destUrl = static_cast(model())->urlForIndex(index); FileOperations::showMenuForDroppedUrls(this, urlList, destUrl); } private: QRect mDropRect; }; FolderViewContextManagerItem::FolderViewContextManagerItem(ContextManager* manager) : AbstractContextManagerItem(manager) { mModel = nullptr; setupView(); connect(contextManager(), SIGNAL(currentDirUrlChanged(QUrl)), SLOT(slotCurrentDirUrlChanged(QUrl))); } void FolderViewContextManagerItem::slotCurrentDirUrlChanged(const QUrl &url) { if (url.isValid() && mUrlToSelect != url) { mUrlToSelect = url.adjusted(QUrl::StripTrailingSlash | QUrl::NormalizePathSegments); mExpandingIndex = QModelIndex(); } if (!mView->isVisible()) { return; } expandToSelectedUrl(); } void FolderViewContextManagerItem::expandToSelectedUrl() { if (!mUrlToSelect.isValid()) { return; } if (!mModel) { setupModel(); } QModelIndex index = findClosestIndex(mExpandingIndex, mUrlToSelect); if (!index.isValid()) { return; } mExpandingIndex = index; QUrl url = mModel->urlForIndex(mExpandingIndex); if (mUrlToSelect == url) { // We found our url QItemSelectionModel* selModel = mView->selectionModel(); selModel->setCurrentIndex(mExpandingIndex, QItemSelectionModel::ClearAndSelect); mView->scrollTo(mExpandingIndex); mUrlToSelect = QUrl(); mExpandingIndex = QModelIndex(); } else { // We found a parent of our url mView->setExpanded(mExpandingIndex, true); } } void FolderViewContextManagerItem::slotRowsInserted(const QModelIndex& parentIndex, int /*start*/, int /*end*/) { // Can't trigger the case where parentIndex is invalid, but it most // probably happen when root items are created. In this case we trigger // expandToSelectedUrl without checking the url. // See bug #191771 if (!parentIndex.isValid() || mModel->urlForIndex(parentIndex).isParentOf(mUrlToSelect)) { mExpandingIndex = parentIndex; // Hack because otherwise indexes are not in correct order! QMetaObject::invokeMethod(this, "expandToSelectedUrl", Qt::QueuedConnection); } } void FolderViewContextManagerItem::slotActivated(const QModelIndex& index) { if (!index.isValid()) { return; } QUrl url = mModel->urlForIndex(index); emit urlChanged(url); } void FolderViewContextManagerItem::setupModel() { mModel = new MODEL_CLASS(this); mView->setModel(mModel); #ifndef USE_PLACETREE for (int col = 1; col <= mModel->columnCount(); ++col) { mView->header()->setSectionHidden(col, true); } mModel->dirLister()->openUrl(QUrl("/")); #endif QObject::connect(mModel, &MODEL_CLASS::rowsInserted, this, &FolderViewContextManagerItem::slotRowsInserted); } void FolderViewContextManagerItem::setupView() { mView = new UrlDropTreeView; mView->setEditTriggers(QAbstractItemView::NoEditTriggers); mView->setAcceptDrops(true); mView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); mView->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); // Necessary to get the drop target highlighted mView->viewport()->setAttribute(Qt::WA_Hover); mView->setHeaderHidden(true); // This is tricky: QTreeView header has stretchLastSection set to true. // In this configuration, the header gets quite wide and cause an // horizontal scrollbar to appear. // To avoid this, set stretchLastSection to false and resizeMode to // Stretch (we still want the column to take the full width of the // widget). mView->header()->setStretchLastSection(false); mView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); setWidget(mView); QObject::connect(mView, &QTreeView::activated, this, &FolderViewContextManagerItem::slotActivated); EventWatcher::install(mView, QEvent::Show, this, SLOT(expandToSelectedUrl())); } QModelIndex FolderViewContextManagerItem::findClosestIndex(const QModelIndex& parent, const QUrl& wantedUrl) { Q_ASSERT(mModel); QModelIndex index = parent; if (!index.isValid()) { index = findRootIndex(wantedUrl); if (!index.isValid()) { return QModelIndex(); } } QUrl url = mModel->urlForIndex(index); if (!url.isParentOf(wantedUrl)) { qWarning() << url << "is not a parent of" << wantedUrl << "!"; return QModelIndex(); } QString relativePath = QDir(url.path()).relativeFilePath(wantedUrl.path()); QModelIndex lastFoundIndex = index; Q_FOREACH(const QString & pathPart, relativePath.split(QDir::separator(), QString::SkipEmptyParts)) { bool found = false; for (int row = 0; row < mModel->rowCount(lastFoundIndex); ++row) { QModelIndex index = mModel->index(row, 0, lastFoundIndex); if (index.data().toString() == pathPart) { // FIXME: Check encoding found = true; lastFoundIndex = index; break; } } if (!found) { break; } } return lastFoundIndex; } QModelIndex FolderViewContextManagerItem::findRootIndex(const QUrl& wantedUrl) { QModelIndex matchIndex; int matchUrlLength = 0; for (int row = 0; row < mModel->rowCount(); ++row) { QModelIndex index = mModel->index(row, 0); QUrl url = mModel->urlForIndex(index); int urlLength = url.url().length(); if (url.isParentOf(wantedUrl) && urlLength > matchUrlLength) { matchIndex = index; matchUrlLength = urlLength; } } if (!matchIndex.isValid()) { qWarning() << "Found no root index for" << wantedUrl; } return matchIndex; } } // namespace diff --git a/app/gvcore.cpp b/app/gvcore.cpp index f90dc40f..a57670a2 100644 --- a/app/gvcore.cpp +++ b/app/gvcore.cpp @@ -1,474 +1,474 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 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, Cambridge, MA 02110-1301, USA. */ // Self #include "gvcore.h" #include "dialogguard.h" // Qt #include #include #include #include #include // KDE #include #include #include #include // Local #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Gwenview { struct GvCorePrivate { GvCore* q; MainWindow* mMainWindow; SortedDirModel* mDirModel; HistoryModel* mRecentFoldersModel; RecentFilesModel* mRecentFilesModel; QPalette mPalettes[4]; QString mFullScreenPaletteName; bool showSaveAsDialog(const QUrl &url, QUrl* outUrl, QByteArray* format) { DialogGuard dialog(mMainWindow); dialog->setAcceptMode(QFileDialog::AcceptSave); dialog->setWindowTitle(i18nc("@title:window", "Save Image")); // Temporary workaround for selectUrl() not setting the // initial directory to url (removed in D4193) dialog->setDirectoryUrl(url.adjusted(QUrl::RemoveFilename)); dialog->selectUrl(url); QStringList supportedMimetypes; for (const QByteArray &mimeName : QImageWriter::supportedMimeTypes()) { supportedMimetypes.append(QString::fromLocal8Bit(mimeName)); } dialog->setMimeTypeFilters(supportedMimetypes); dialog->selectMimeTypeFilter(MimeTypeUtils::urlMimeType(url)); // Show dialog do { if (!dialog->exec()) { return false; } QList files = dialog->selectedUrls(); if (files.isEmpty()) { return false; } QString filename = files.first().fileName(); const QMimeType mimeType = QMimeDatabase().mimeTypeForFile(filename, QMimeDatabase::MatchExtension); if (mimeType.isValid()) { *format = mimeType.preferredSuffix().toLocal8Bit(); break; } KMessageBox::sorry( mMainWindow, i18nc("@info", "Gwenview cannot save images as %1.", QFileInfo(filename).suffix()) ); } while (true); *outUrl = dialog->selectedUrls().first(); return true; } void setupPalettes() { QPalette pal; int value = GwenviewConfig::viewBackgroundValue(); QColor fgColor = value > 128 ? Qt::black : Qt::white; // Normal KSharedConfigPtr config = KSharedConfig::openConfig(); mPalettes[GvCore::NormalPalette] = KColorScheme::createApplicationPalette(config); pal = mPalettes[GvCore::NormalPalette]; pal.setColor(QPalette::Base, QColor::fromHsv(0, 0, value)); pal.setColor(QPalette::Text, fgColor); mPalettes[GvCore::NormalViewPalette] = pal; // Fullscreen QString name = GwenviewConfig::fullScreenColorScheme(); if (name.isEmpty()) { // Default color scheme mFullScreenPaletteName = QStandardPaths::locate(QStandardPaths::AppDataLocation, "color-schemes/fullscreen.colors"); config = KSharedConfig::openConfig(mFullScreenPaletteName); } else if (name.contains('/')) { // Full path to a .colors file mFullScreenPaletteName = name; config = KSharedConfig::openConfig(mFullScreenPaletteName); } else { // Standard KDE color scheme - mFullScreenPaletteName = QString("color-schemes/%1.colors").arg(name); + mFullScreenPaletteName = QStringLiteral("color-schemes/%1.colors").arg(name); config = KSharedConfig::openConfig(mFullScreenPaletteName, KConfig::FullConfig, QStandardPaths::AppDataLocation); } mPalettes[GvCore::FullScreenPalette] = KColorScheme::createApplicationPalette(config); // If we are using the default palette, adjust it to match the system color scheme if (name.isEmpty()) { adjustDefaultFullScreenPalette(); } // FullScreenView has textured background pal = mPalettes[GvCore::FullScreenPalette]; QString path = QStandardPaths::locate(QStandardPaths::AppDataLocation, "images/background.png"); QPixmap bgTexture(path); pal.setBrush(QPalette::Base, bgTexture); mPalettes[GvCore::FullScreenViewPalette] = pal; } void adjustDefaultFullScreenPalette() { // The Fullscreen palette by default does not use the system color scheme, and therefore uses an 'accent' color // of blue. So for every color group/role combination that uses the accent color, we use a muted version of the // Normal palette. We also use the normal HighlightedText color so it properly contrasts with Highlight. const QPalette normalPal = mPalettes[GvCore::NormalPalette]; QPalette fullscreenPal = mPalettes[GvCore::FullScreenPalette]; // Colors from the normal palette (source of the system theme's accent color) const QColor normalToolTipBase = normalPal.color(QPalette::Normal, QPalette::ToolTipBase); const QColor normalToolTipText = normalPal.color(QPalette::Normal, QPalette::ToolTipText); const QColor normalHighlight = normalPal.color(QPalette::Normal, QPalette::Highlight); const QColor normalHighlightedText = normalPal.color(QPalette::Normal, QPalette::HighlightedText); const QColor normalLink = normalPal.color(QPalette::Normal, QPalette::Link); const QColor normalActiveToolTipBase = normalPal.color(QPalette::Active, QPalette::ToolTipBase); const QColor normalActiveToolTipText = normalPal.color(QPalette::Active, QPalette::ToolTipText); const QColor normalActiveHighlight = normalPal.color(QPalette::Active, QPalette::Highlight); const QColor normalActiveHighlightedText = normalPal.color(QPalette::Active, QPalette::HighlightedText); const QColor normalActiveLink = normalPal.color(QPalette::Active, QPalette::Link); const QColor normalDisabledToolTipBase = normalPal.color(QPalette::Disabled, QPalette::ToolTipBase); const QColor normalDisabledToolTipText = normalPal.color(QPalette::Disabled, QPalette::ToolTipText); // Note: Disabled Highlight missing as they do not use the accent color const QColor normalDisabledLink = normalPal.color(QPalette::Disabled, QPalette::Link); const QColor normalInactiveToolTipBase = normalPal.color(QPalette::Inactive, QPalette::ToolTipBase); const QColor normalInactiveToolTipText = normalPal.color(QPalette::Inactive, QPalette::ToolTipText); const QColor normalInactiveHighlight = normalPal.color(QPalette::Inactive, QPalette::Highlight); const QColor normalInactiveHighlightedText = normalPal.color(QPalette::Inactive, QPalette::HighlightedText); const QColor normalInactiveLink = normalPal.color(QPalette::Inactive, QPalette::Link); // Colors of the fullscreen palette which we will be modifying QColor fullScreenToolTipBase = fullscreenPal.color(QPalette::Normal, QPalette::ToolTipBase); QColor fullScreenToolTipText = fullscreenPal.color(QPalette::Normal, QPalette::ToolTipText); QColor fullScreenHighlight = fullscreenPal.color(QPalette::Normal, QPalette::Highlight); QColor fullScreenLink = fullscreenPal.color(QPalette::Normal, QPalette::Link); QColor fullScreenActiveToolTipBase = fullscreenPal.color(QPalette::Active, QPalette::ToolTipBase); QColor fullScreenActiveToolTipText = fullscreenPal.color(QPalette::Active, QPalette::ToolTipText); QColor fullScreenActiveHighlight = fullscreenPal.color(QPalette::Active, QPalette::Highlight); QColor fullScreenActiveLink = fullscreenPal.color(QPalette::Active, QPalette::Link); QColor fullScreenDisabledToolTipBase = fullscreenPal.color(QPalette::Disabled, QPalette::ToolTipBase); QColor fullScreenDisabledToolTipText = fullscreenPal.color(QPalette::Disabled, QPalette::ToolTipText); QColor fullScreenDisabledLink = fullscreenPal.color(QPalette::Disabled, QPalette::Link); QColor fullScreenInactiveToolTipBase = fullscreenPal.color(QPalette::Inactive, QPalette::ToolTipBase); QColor fullScreenInactiveToolTipText = fullscreenPal.color(QPalette::Inactive, QPalette::ToolTipText); QColor fullScreenInactiveHighlight = fullscreenPal.color(QPalette::Inactive, QPalette::Highlight); QColor fullScreenInactiveLink = fullscreenPal.color(QPalette::Inactive, QPalette::Link); // Adjust the value of the normal color so it's not too dark/bright, and apply to the respective fullscreen color fullScreenToolTipBase .setHsv(normalToolTipBase.hue(), normalToolTipBase.saturation(), (127 + 2 * normalToolTipBase.value()) / 3); fullScreenToolTipText .setHsv(normalToolTipText.hue(), normalToolTipText.saturation(), (127 + 2 * normalToolTipText.value()) / 3); fullScreenHighlight .setHsv(normalHighlight.hue(), normalHighlight.saturation(), (127 + 2 * normalHighlight.value()) / 3); fullScreenLink .setHsv(normalLink.hue(), normalLink.saturation(), (127 + 2 * normalLink.value()) / 3); fullScreenActiveToolTipBase .setHsv(normalActiveToolTipBase.hue(), normalActiveToolTipBase.saturation(), (127 + 2 * normalActiveToolTipBase.value()) / 3); fullScreenActiveToolTipText .setHsv(normalActiveToolTipText.hue(), normalActiveToolTipText.saturation(), (127 + 2 * normalActiveToolTipText.value()) / 3); fullScreenActiveHighlight .setHsv(normalActiveHighlight.hue(), normalActiveHighlight.saturation(), (127 + 2 * normalActiveHighlight.value()) / 3); fullScreenActiveLink .setHsv(normalActiveLink.hue(), normalActiveLink.saturation(), (127 + 2 * normalActiveLink.value()) / 3); fullScreenDisabledToolTipBase.setHsv(normalDisabledToolTipBase.hue(), normalDisabledToolTipBase.saturation(), (127 + 2 * normalDisabledToolTipBase.value()) / 3); fullScreenDisabledToolTipText.setHsv(normalDisabledToolTipText.hue(), normalDisabledToolTipText.saturation(), (127 + 2 * normalDisabledToolTipText.value()) / 3); fullScreenDisabledLink .setHsv(normalDisabledLink.hue(), normalDisabledLink.saturation(), (127 + 2 * normalDisabledLink.value()) / 3); fullScreenInactiveToolTipBase.setHsv(normalInactiveToolTipBase.hue(), normalInactiveToolTipBase.saturation(), (127 + 2 * normalInactiveToolTipBase.value()) / 3); fullScreenInactiveToolTipText.setHsv(normalInactiveToolTipText.hue(), normalInactiveToolTipText.saturation(), (127 + 2 * normalInactiveToolTipText.value()) / 3); fullScreenInactiveHighlight .setHsv(normalInactiveHighlight.hue(), normalInactiveHighlight.saturation(), (127 + 2 * normalInactiveHighlight.value()) / 3); fullScreenInactiveLink .setHsv(normalInactiveLink.hue(), normalInactiveLink.saturation(), (127 + 2 * normalInactiveLink.value()) / 3); // Apply the modified colors to the fullscreen palette fullscreenPal.setColor(QPalette::Normal, QPalette::ToolTipBase, fullScreenToolTipBase); fullscreenPal.setColor(QPalette::Normal, QPalette::ToolTipText, fullScreenToolTipText); fullscreenPal.setColor(QPalette::Normal, QPalette::Highlight, fullScreenHighlight); fullscreenPal.setColor(QPalette::Normal, QPalette::Link, fullScreenLink); fullscreenPal.setColor(QPalette::Active, QPalette::ToolTipBase, fullScreenActiveToolTipBase); fullscreenPal.setColor(QPalette::Active, QPalette::ToolTipText, fullScreenActiveToolTipText); fullscreenPal.setColor(QPalette::Active, QPalette::Highlight, fullScreenActiveHighlight); fullscreenPal.setColor(QPalette::Active, QPalette::Link, fullScreenActiveLink); fullscreenPal.setColor(QPalette::Disabled, QPalette::ToolTipBase, fullScreenDisabledToolTipBase); fullscreenPal.setColor(QPalette::Disabled, QPalette::ToolTipText, fullScreenDisabledToolTipText); fullscreenPal.setColor(QPalette::Disabled, QPalette::Link, fullScreenDisabledLink); fullscreenPal.setColor(QPalette::Inactive, QPalette::ToolTipBase, fullScreenInactiveToolTipBase); fullscreenPal.setColor(QPalette::Inactive, QPalette::ToolTipText, fullScreenInactiveToolTipText); fullscreenPal.setColor(QPalette::Inactive, QPalette::Highlight, fullScreenInactiveHighlight); fullscreenPal.setColor(QPalette::Inactive, QPalette::Link, fullScreenInactiveLink); // Since we use an adjusted version of the normal highlight color, we need to use the normal version of the // text color so it contrasts fullscreenPal.setColor(QPalette::Normal, QPalette::HighlightedText, normalHighlightedText); fullscreenPal.setColor(QPalette::Active, QPalette::HighlightedText, normalActiveHighlightedText); fullscreenPal.setColor(QPalette::Inactive, QPalette::HighlightedText, normalInactiveHighlightedText); mPalettes[GvCore::FullScreenPalette] = fullscreenPal; } }; GvCore::GvCore(MainWindow* mainWindow, SortedDirModel* dirModel) : QObject(mainWindow) , d(new GvCorePrivate) { d->q = this; d->mMainWindow = mainWindow; d->mDirModel = dirModel; d->mRecentFoldersModel = nullptr; d->mRecentFilesModel = nullptr; d->setupPalettes(); connect(GwenviewConfig::self(), SIGNAL(configChanged()), SLOT(slotConfigChanged())); } GvCore::~GvCore() { delete d; } QAbstractItemModel* GvCore::recentFoldersModel() const { if (!d->mRecentFoldersModel) { d->mRecentFoldersModel = new HistoryModel(const_cast(this), QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/recentfolders/"); } return d->mRecentFoldersModel; } QAbstractItemModel* GvCore::recentFilesModel() const { if (!d->mRecentFilesModel) { d->mRecentFilesModel = new RecentFilesModel(const_cast(this)); } return d->mRecentFilesModel; } AbstractSemanticInfoBackEnd* GvCore::semanticInfoBackEnd() const { return d->mDirModel->semanticInfoBackEnd(); } SortedDirModel* GvCore::sortedDirModel() const { return d->mDirModel; } void GvCore::addUrlToRecentFolders(QUrl url) { if (!GwenviewConfig::historyEnabled()) { return; } if (!url.isValid()) { return; } // For "sftp://localhost", "/" is a different path than "" (bug #312060) if (!url.path().isEmpty() && !url.path().endsWith('/')) { url.setPath(url.path() + '/'); } recentFoldersModel(); d->mRecentFoldersModel->addUrl(url); } void GvCore::addUrlToRecentFiles(const QUrl &url) { if (!GwenviewConfig::historyEnabled()) { return; } recentFilesModel(); d->mRecentFilesModel->addUrl(url); } void GvCore::saveAll() { SaveAllHelper helper(d->mMainWindow); helper.save(); } void GvCore::save(const QUrl &url) { Document::Ptr doc = DocumentFactory::instance()->load(url); QByteArray format = doc->format(); const QByteArrayList availableTypes = QImageWriter::supportedImageFormats(); if (availableTypes.contains(format)) { DocumentJob* job = doc->save(url, format); connect(job, SIGNAL(result(KJob*)), SLOT(slotSaveResult(KJob*))); } else { // We don't know how to save in 'format', ask the user for a format we can // write to. KGuiItem saveUsingAnotherFormat = KStandardGuiItem::saveAs(); saveUsingAnotherFormat.setText(i18n("Save using another format")); int result = KMessageBox::warningContinueCancel( d->mMainWindow, i18n("Gwenview cannot save images in '%1' format.", QString(format)), QString() /* caption */, saveUsingAnotherFormat ); if (result == KMessageBox::Continue) { saveAs(url); } } } void GvCore::saveAs(const QUrl &url) { QByteArray format; QUrl saveAsUrl; if (!d->showSaveAsDialog(url, &saveAsUrl, &format)) { return; } // Start save Document::Ptr doc = DocumentFactory::instance()->load(url); KJob* job = doc->save(saveAsUrl, format.data()); if (!job) { const QString name = saveAsUrl.fileName().isEmpty() ? saveAsUrl.toDisplayString() : saveAsUrl.fileName(); const QString msg = xi18nc("@info", "Saving %1 failed:%2", name, doc->errorString()); KMessageBox::sorry(QApplication::activeWindow(), msg); } else { connect(job, SIGNAL(result(KJob*)), SLOT(slotSaveResult(KJob*))); } } static void applyTransform(const QUrl &url, Orientation orientation) { TransformImageOperation* op = new TransformImageOperation(orientation); Document::Ptr doc = DocumentFactory::instance()->load(url); op->applyToDocument(doc); } void GvCore::slotSaveResult(KJob* _job) { SaveJob* job = static_cast(_job); QUrl oldUrl = job->oldUrl(); QUrl newUrl = job->newUrl(); if (job->error()) { QString name = newUrl.fileName().isEmpty() ? newUrl.toDisplayString() : newUrl.fileName(); const QString msg = xi18nc("@info", "Saving %1 failed:%2", name, kxi18n(qPrintable(job->errorString()))); int result = KMessageBox::warningContinueCancel( d->mMainWindow, msg, QString() /* caption */, KStandardGuiItem::saveAs()); if (result == KMessageBox::Continue) { saveAs(oldUrl); } return; } if (oldUrl != newUrl) { d->mMainWindow->goToUrl(newUrl); ViewMainPage* page = d->mMainWindow->viewMainPage(); if (page->isVisible()) { HudMessageBubble* bubble = new HudMessageBubble(); bubble->setText(i18n("You are now viewing the new document.")); KGuiItem item = KStandardGuiItem::back(); item.setText(i18n("Go back to the original")); HudButton* button = bubble->addButton(item); BinderRef::bind(button, SIGNAL(clicked()), d->mMainWindow, &MainWindow::goToUrl, oldUrl); connect(button, SIGNAL(clicked()), bubble, SLOT(deleteLater())); page->showMessageWidget(bubble); } } } void GvCore::rotateLeft(const QUrl &url) { applyTransform(url, ROT_270); } void GvCore::rotateRight(const QUrl &url) { applyTransform(url, ROT_90); } void GvCore::setRating(const QUrl &url, int rating) { QModelIndex index = d->mDirModel->indexForUrl(url); if (!index.isValid()) { qWarning() << "invalid index!"; return; } d->mDirModel->setData(index, rating, SemanticInfoDirModel::RatingRole); } static void clearModel(QAbstractItemModel* model) { model->removeRows(0, model->rowCount()); } void GvCore::clearRecentFilesAndFolders() { clearModel(recentFilesModel()); clearModel(recentFoldersModel()); } void GvCore::slotConfigChanged() { if (!GwenviewConfig::historyEnabled()) { clearRecentFilesAndFolders(); } d->setupPalettes(); } QPalette GvCore::palette(GvCore::PaletteType type) const { return d->mPalettes[type]; } QString GvCore::fullScreenPaletteName() const { return d->mFullScreenPaletteName; } } // namespace diff --git a/app/imagemetainfodialog.cpp b/app/imagemetainfodialog.cpp index 544f58c1..7ca2242a 100644 --- a/app/imagemetainfodialog.cpp +++ b/app/imagemetainfodialog.cpp @@ -1,165 +1,165 @@ // 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 "imagemetainfodialog.h" // Qt #include #include #include #include #include #include #include #include // KDE #include // STL #include // Local #include namespace Gwenview { class MetaInfoDelegate : public QStyledItemDelegate { public: MetaInfoDelegate(QObject* parent) : QStyledItemDelegate(parent) {} protected: - void paint(QPainter* painter, const QStyleOptionViewItem& _option, const QModelIndex& index) const Q_DECL_OVERRIDE + void paint(QPainter* painter, const QStyleOptionViewItem& _option, const QModelIndex& index) const override { QStyleOptionViewItem option = _option; if (!index.parent().isValid()) { option.displayAlignment = Qt::AlignCenter | Qt::AlignBottom; option.font.setBold(true); } QStyledItemDelegate::paint(painter, option, index); if (!index.parent().isValid()) { painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight()); } } - QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const Q_DECL_OVERRIDE + QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override { QSize sh = QStyledItemDelegate::sizeHint(option, index); if (!index.parent().isValid()) { sh.setHeight(sh.height() * 3 / 2); } return sh; } }; /** * A tree view which is always fully expanded */ class ExpandedTreeView : public QTreeView { public: explicit ExpandedTreeView(QWidget* parent) : QTreeView(parent) {} protected: - void rowsInserted(const QModelIndex& parent, int start, int end) Q_DECL_OVERRIDE + void rowsInserted(const QModelIndex& parent, int start, int end) override { QTreeView::rowsInserted(parent, start, end); if (!parent.isValid()) { for (int row = start; row <= end; ++row) { setUpRootIndex(row); } } } - void reset() Q_DECL_OVERRIDE + void reset() override { QTreeView::reset(); if (model()) { for (int row = 0; row < model()->rowCount(); ++row) { setUpRootIndex(row); } } } private: void setUpRootIndex(int row) { expand(model()->index(row, 0)); setFirstColumnSpanned(row, QModelIndex(), true); } }; struct ImageMetaInfoDialogPrivate { std::unique_ptr mModel; QTreeView* mTreeView; }; ImageMetaInfoDialog::ImageMetaInfoDialog(QWidget* parent) : QDialog(parent) , d(new ImageMetaInfoDialogPrivate) { d->mTreeView = new ExpandedTreeView(this); d->mTreeView->setRootIsDecorated(false); d->mTreeView->setIndentation(0); d->mTreeView->setItemDelegate(new MetaInfoDelegate(d->mTreeView)); setWindowTitle(i18nc("@title:window", "Image Information")); setLayout(new QVBoxLayout); layout()->addWidget(d->mTreeView); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); layout()->addWidget(buttonBox); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); } ImageMetaInfoDialog::~ImageMetaInfoDialog() { delete d; } void ImageMetaInfoDialog::setMetaInfo(ImageMetaInfoModel* model, const QStringList& list) { if (model) { d->mModel.reset(new PreferredImageMetaInfoModel(model, list)); connect(d->mModel.get(), SIGNAL(preferredMetaInfoKeyListChanged(QStringList)), this, SIGNAL(preferredMetaInfoKeyListChanged(QStringList))); } else { d->mModel.reset(nullptr); } d->mTreeView->setModel(d->mModel.get()); const int marginSize = QApplication::style()->pixelMetric(QStyle::PM_DefaultChildMargin); d->mTreeView->header()->resizeSection(0, sizeHint().width() / 2 - marginSize * 2); } QSize ImageMetaInfoDialog::sizeHint() const { return QSize(400, 300); } } // namespace diff --git a/app/imagemetainfodialog.h b/app/imagemetainfodialog.h index 900add62..b1122415 100644 --- a/app/imagemetainfodialog.h +++ b/app/imagemetainfodialog.h @@ -1,60 +1,60 @@ // 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. */ #ifndef IMAGEMETAINFODIALOG_H #define IMAGEMETAINFODIALOG_H // Qt // KDE #include // Local namespace Gwenview { class ImageMetaInfoModel; struct ImageMetaInfoDialogPrivate; class ImageMetaInfoDialog : public QDialog { Q_OBJECT public: explicit ImageMetaInfoDialog(QWidget* parent); - ~ImageMetaInfoDialog() Q_DECL_OVERRIDE; + ~ImageMetaInfoDialog() override; /** * Defines the image metainfo model and the preferred metainfo key list. */ void setMetaInfo(ImageMetaInfoModel*, const QStringList& list); - QSize sizeHint() const Q_DECL_OVERRIDE; + QSize sizeHint() const override; Q_SIGNALS: void preferredMetaInfoKeyListChanged(const QStringList&); private: ImageMetaInfoDialogPrivate* const d; }; } // namespace #endif /* IMAGEMETAINFODIALOG_H */ diff --git a/app/infocontextmanageritem.cpp b/app/infocontextmanageritem.cpp index c3a6a777..186d58ec 100644 --- a/app/infocontextmanageritem.cpp +++ b/app/infocontextmanageritem.cpp @@ -1,383 +1,383 @@ /* 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. */ #include "infocontextmanageritem.h" // Qt #include #include #include #include #include // KDE #include #include // Local #include "imagemetainfodialog.h" #include "sidebar.h" #include #include #include #include #include #include #include #include namespace Gwenview { #undef ENABLE_LOG #undef LOG //#define ENABLE_LOG #ifdef ENABLE_LOG #define LOG(x) qDebug() << x #else #define LOG(x) ; #endif /** * This widget is capable of showing multiple lines of key/value pairs. */ class KeyValueWidget : public QWidget { struct Row { Row(QWidget* parent) : keyLabel(new QLabel(parent)) , valueLabel(new QLabel(parent)) { initLabel(keyLabel); initLabel(valueLabel); QPalette pal = keyLabel->palette(); QColor color = pal.color(QPalette::WindowText); color.setAlphaF(0.65); pal.setColor(QPalette::WindowText, color); keyLabel->setPalette(pal); valueLabel->setContentsMargins(6, 0, 0, 6); } ~Row() { delete keyLabel; delete valueLabel; } int setLabelGeometries(int rowY, int labelWidth) { int labelHeight = keyLabel->heightForWidth(labelWidth); keyLabel->setGeometry(0, rowY, labelWidth, labelHeight); rowY += labelHeight; labelHeight = valueLabel->heightForWidth(labelWidth); valueLabel->setGeometry(0, rowY, labelWidth, labelHeight); rowY += labelHeight; return rowY; } int heightForWidth(int width) const { return keyLabel->heightForWidth(width) + valueLabel->heightForWidth(width); } static void initLabel(QLabel* label) { label->setWordWrap(true); label->show(); label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse); } QLabel* keyLabel; QLabel* valueLabel; }; public: explicit KeyValueWidget(QWidget* parent = nullptr) : QWidget(parent) { QSizePolicy policy(QSizePolicy::Preferred, QSizePolicy::Fixed); policy.setHeightForWidth(true); setSizePolicy(policy); } - QSize sizeHint() const Q_DECL_OVERRIDE + QSize sizeHint() const override { int width = 150; int height = heightForWidth(width); return QSize(width, height); } - int heightForWidth(int w) const Q_DECL_OVERRIDE + int heightForWidth(int w) const override { int height = 0; Q_FOREACH(Row* row, mRows) { height += row->heightForWidth(w); } return height; } void clear() { qDeleteAll(mRows); mRows.clear(); updateGeometry(); } void addRow(const QString& key, const QString& value) { Row* row = new Row(this); row->keyLabel->setText(i18nc( "@item:intext %1 is a key, we append a colon to it. A value is displayed after", "%1:", key)); row->valueLabel->setText(value); mRows << row; } static bool rowsLessThan(const Row* row1, const Row* row2) { return row1->keyLabel->text() < row2->keyLabel->text(); } void finishAddRows() { qSort(mRows.begin(), mRows.end(), KeyValueWidget::rowsLessThan); updateGeometry(); } void layoutRows() { // Layout labels manually: I tried to use a QVBoxLayout but for some // reason when using software animations the widget blinks when going // from one image to another int rowY = 0; const int labelWidth = width(); Q_FOREACH(Row* row, mRows) { rowY = row->setLabelGeometries(rowY, labelWidth); } } protected: - void showEvent(QShowEvent* event) Q_DECL_OVERRIDE + void showEvent(QShowEvent* event) override { QWidget::showEvent(event); layoutRows(); } - void resizeEvent(QResizeEvent* event) Q_DECL_OVERRIDE + void resizeEvent(QResizeEvent* event) override { QWidget::resizeEvent(event); layoutRows(); } private: QVector mRows; }; struct InfoContextManagerItemPrivate { InfoContextManagerItem* q; SideBarGroup* mGroup; // One selection fields QScrollArea* mOneFileWidget; KeyValueWidget* mKeyValueWidget; Document::Ptr mDocument; // Multiple selection fields QLabel* mMultipleFilesLabel; QPointer mImageMetaInfoDialog; void updateMetaInfoDialog() { if (!mImageMetaInfoDialog) { return; } ImageMetaInfoModel* model = mDocument ? mDocument->metaInfo() : nullptr; mImageMetaInfoDialog->setMetaInfo(model, GwenviewConfig::preferredMetaInfoKeyList()); } void setupGroup() { mOneFileWidget = new QScrollArea(); mOneFileWidget->setFrameStyle(QFrame::NoFrame); mOneFileWidget->setWidgetResizable(true); mKeyValueWidget = new KeyValueWidget; QLabel* moreLabel = new QLabel(mOneFileWidget); - moreLabel->setText(QString("%1").arg(i18nc("@action show more image meta info", "More..."))); + moreLabel->setText(QStringLiteral("%1").arg(i18nc("@action show more image meta info", "More..."))); moreLabel->setAlignment(Qt::AlignRight); QWidget* content = new QWidget; QVBoxLayout* layout = new QVBoxLayout(content); layout->setMargin(2); layout->setSpacing(2); layout->addWidget(mKeyValueWidget); layout->addWidget(moreLabel); mOneFileWidget->setWidget(content); mMultipleFilesLabel = new QLabel(); mGroup = new SideBarGroup(i18nc("@title:group", "Meta Information")); q->setWidget(mGroup); mGroup->addWidget(mOneFileWidget); mGroup->addWidget(mMultipleFilesLabel); EventWatcher::install(mGroup, QEvent::Show, q, SLOT(updateSideBarContent())); QObject::connect(moreLabel, &QLabel::linkActivated, q, &InfoContextManagerItem::showMetaInfoDialog); } void forgetCurrentDocument() { if (mDocument) { QObject::disconnect(mDocument.data(), nullptr, q, nullptr); // "Garbage collect" document mDocument = nullptr; } } }; InfoContextManagerItem::InfoContextManagerItem(ContextManager* manager) : AbstractContextManagerItem(manager) , d(new InfoContextManagerItemPrivate) { d->q = this; d->setupGroup(); connect(contextManager(), SIGNAL(selectionChanged()), SLOT(updateSideBarContent())); connect(contextManager(), SIGNAL(selectionDataChanged()), SLOT(updateSideBarContent())); } InfoContextManagerItem::~InfoContextManagerItem() { delete d; } void InfoContextManagerItem::updateSideBarContent() { LOG("updateSideBarContent"); if (!d->mGroup->isVisible()) { LOG("updateSideBarContent: not visible, not updating"); return; } LOG("updateSideBarContent: really updating"); KFileItemList itemList = contextManager()->selectedFileItemList(); if (itemList.count() == 0) { d->forgetCurrentDocument(); d->mOneFileWidget->hide(); d->mMultipleFilesLabel->hide(); d->updateMetaInfoDialog(); return; } KFileItem item = itemList.first(); if (itemList.count() == 1 && !ArchiveUtils::fileItemIsDirOrArchive(item)) { fillOneFileGroup(item); } else { fillMultipleItemsGroup(itemList); } d->updateMetaInfoDialog(); } void InfoContextManagerItem::fillOneFileGroup(const KFileItem& item) { d->mOneFileWidget->show(); d->mMultipleFilesLabel->hide(); d->forgetCurrentDocument(); d->mDocument = DocumentFactory::instance()->load(item.url()); connect(d->mDocument.data(), SIGNAL(metaInfoUpdated()), SLOT(updateOneFileInfo())); d->updateMetaInfoDialog(); updateOneFileInfo(); } void InfoContextManagerItem::fillMultipleItemsGroup(const KFileItemList& itemList) { d->forgetCurrentDocument(); int folderCount = 0, fileCount = 0; Q_FOREACH(const KFileItem & item, itemList) { if (item.isDir()) { folderCount++; } else { fileCount++; } } if (folderCount == 0) { d->mMultipleFilesLabel->setText(i18ncp("@label", "%1 file selected", "%1 files selected", fileCount)); } else if (fileCount == 0) { d->mMultipleFilesLabel->setText(i18ncp("@label", "%1 folder selected", "%1 folders selected", folderCount)); } else { d->mMultipleFilesLabel->setText(i18nc("@label. The two parameters are strings like '2 folders' and '1 file'.", "%1 and %2 selected", i18np("%1 folder", "%1 folders", folderCount), i18np("%1 file", "%1 files", fileCount))); } d->mOneFileWidget->hide(); d->mMultipleFilesLabel->show(); } void InfoContextManagerItem::updateOneFileInfo() { if (!d->mDocument) { return; } ImageMetaInfoModel* metaInfoModel = d->mDocument->metaInfo(); d->mKeyValueWidget->clear(); Q_FOREACH(const QString & key, GwenviewConfig::preferredMetaInfoKeyList()) { QString label; QString value; metaInfoModel->getInfoForKey(key, &label, &value); if (!label.isEmpty() && !value.isEmpty()) { d->mKeyValueWidget->addRow(label, value); } } d->mKeyValueWidget->finishAddRows(); d->mKeyValueWidget->layoutRows(); } void InfoContextManagerItem::showMetaInfoDialog() { if (!d->mImageMetaInfoDialog) { d->mImageMetaInfoDialog = new ImageMetaInfoDialog(d->mOneFileWidget); d->mImageMetaInfoDialog->setAttribute(Qt::WA_DeleteOnClose, true); connect(d->mImageMetaInfoDialog, SIGNAL(preferredMetaInfoKeyListChanged(QStringList)), SLOT(slotPreferredMetaInfoKeyListChanged(QStringList))); } d->mImageMetaInfoDialog->setMetaInfo(d->mDocument ? d->mDocument->metaInfo() : nullptr, GwenviewConfig::preferredMetaInfoKeyList()); d->mImageMetaInfoDialog->show(); } void InfoContextManagerItem::slotPreferredMetaInfoKeyListChanged(const QStringList& list) { GwenviewConfig::setPreferredMetaInfoKeyList(list); GwenviewConfig::self()->save(); updateOneFileInfo(); } } // namespace diff --git a/app/mainwindow.h b/app/mainwindow.h index cb3ed9eb..4ec7c350 100644 --- a/app/mainwindow.h +++ b/app/mainwindow.h @@ -1,145 +1,145 @@ /* 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. */ #ifndef MAINWINDOW_H #define MAINWINDOW_H // Qt #include // KDE #include class QModelIndex; class QUrl; namespace Gwenview { class ViewMainPage; class ContextManager; class MainWindow : public KXmlGuiWindow { Q_OBJECT public: MainWindow(); - ~MainWindow() Q_DECL_OVERRIDE; + ~MainWindow() override; /** * Defines the url to display when the window is shown for the first time. */ void setInitialUrl(const QUrl&); void startSlideShow(); ViewMainPage* viewMainPage() const; ContextManager* contextManager() const; void setDistractionFreeMode(bool); public Q_SLOTS: void showStartMainPage(); /** * Go to url, without changing current mode */ void goToUrl(const QUrl&); Q_SIGNALS: void viewModeChanged(); public Q_SLOTS: - void setCaption(const QString&) Q_DECL_OVERRIDE; + void setCaption(const QString&) override; - void setCaption(const QString&, bool modified) Q_DECL_OVERRIDE; + void setCaption(const QString&, bool modified) override; protected: - bool queryClose() Q_DECL_OVERRIDE; - QSize sizeHint() const Q_DECL_OVERRIDE; - void showEvent(QShowEvent*) Q_DECL_OVERRIDE; - void resizeEvent(QResizeEvent*) Q_DECL_OVERRIDE; - void saveProperties(KConfigGroup&) Q_DECL_OVERRIDE; - void readProperties(const KConfigGroup&) Q_DECL_OVERRIDE; - bool eventFilter(QObject *, QEvent *) Q_DECL_OVERRIDE; + bool queryClose() override; + QSize sizeHint() const override; + void showEvent(QShowEvent*) override; + void resizeEvent(QResizeEvent*) override; + void saveProperties(KConfigGroup&) override; + void readProperties(const KConfigGroup&) override; + bool eventFilter(QObject *, QEvent *) override; private Q_SLOTS: void setActiveViewModeAction(QAction* action); void openDirUrl(const QUrl&); void slotThumbnailViewIndexActivated(const QModelIndex&); void slotStartMainPageUrlSelected(const QUrl&); void goUp(); void toggleSideBar(bool visible); void slotModifiedDocumentListChanged(); void slotUpdateCaption(const QString& caption); void slotPartCompleted(); void slotDirModelNewItems(); void slotDirListerCompleted(); void slotSelectionChanged(); void slotCurrentDirUrlChanged(const QUrl &url); void goToPrevious(); void goToNext(); void goToFirst(); void goToLast(); void updatePreviousNextActions(); void leaveFullScreen(); void toggleFullScreen(bool); void toggleSlideShow(); void updateSlideShowAction(); void saveCurrent(); void saveCurrentAs(); void openFile(); void openUrl(const QUrl& url); void reload(); void showDocumentInFullScreen(const QUrl&); void showConfigDialog(); void loadConfig(); void print(); void preloadNextUrl(); void toggleMenuBar(); void toggleStatusBar(bool visible); void showFirstDocumentReached(); void showLastDocumentReached(); private: struct Private; MainWindow::Private* const d; void openSelectedDocuments(); void saveConfig(); void configureShortcuts(); void folderViewUrlChanged(const QUrl &url); }; } // namespace #endif /* MAINWINDOW_H */ diff --git a/app/savebar.cpp b/app/savebar.cpp index cce25769..ef160c97 100644 --- a/app/savebar.cpp +++ b/app/savebar.cpp @@ -1,369 +1,369 @@ // 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 "savebar.h" // Qt #include #include #include #include #include #include #include // KDE #include #include #include #include // Local #include "lib/document/documentfactory.h" #include "lib/gwenviewconfig.h" #include "lib/memoryutils.h" #include "lib/paintutils.h" namespace Gwenview { QToolButton* createToolButton() { QToolButton* button = new QToolButton; button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); button->hide(); return button; } struct SaveBarPrivate { SaveBar* q; KActionCollection* mActionCollection; QWidget* mSaveBarWidget; QWidget* mTopRowWidget; QToolButton* mUndoButton; QToolButton* mRedoButton; QToolButton* mSaveCurrentUrlButton; QToolButton* mSaveAsButton; QToolButton* mSaveAllButton; QToolButton* mSaveAllFullScreenButton; QLabel* mMessageLabel; QLabel* mActionsLabel; QFrame* mTooManyChangesFrame; QUrl mCurrentUrl; void createTooManyChangesFrame() { mTooManyChangesFrame = new QFrame; // Icon QLabel* iconLabel = new QLabel; QPixmap pix = KIconLoader::global()->loadIcon( "dialog-warning", KIconLoader::Dialog, KIconLoader::SizeSmall); iconLabel->setPixmap(pix); // Text label QLabel* textLabel = new QLabel; textLabel->setText( i18n("You have modified many images. To avoid memory problems, you should save your changes.") ); mSaveAllFullScreenButton = createToolButton(); // Layout QHBoxLayout* layout = new QHBoxLayout(mTooManyChangesFrame); layout->setMargin(0); layout->addWidget(iconLabel); layout->addWidget(textLabel); layout->addWidget(mSaveAllFullScreenButton); mTooManyChangesFrame->hide(); // CSS KColorScheme scheme(mSaveBarWidget->palette().currentColorGroup(), KColorScheme::Window); QColor warningBackgroundColor = scheme.background(KColorScheme::NegativeBackground).color(); QColor warningBorderColor = PaintUtils::adjustedHsv(warningBackgroundColor, 0, 150, 0); QColor warningColor = scheme.foreground(KColorScheme::NegativeText).color(); QString css = ".QFrame {" " background-color: %1;" " border: 1px solid %2;" " border-radius: 4px;" " padding: 3px;" "}" ".QFrame QLabel {" " color: %3;" "}" ; css = css .arg(warningBackgroundColor.name()) .arg(warningBorderColor.name()) .arg(warningColor.name()) ; mTooManyChangesFrame->setStyleSheet(css); } void applyNormalStyleSheet() { QColor bgColor = QToolTip::palette().base().color(); QColor borderColor = PaintUtils::adjustedHsv(bgColor, 0, 150, 0); QColor fgColor = QToolTip::palette().text().color(); QString css = "#saveBarWidget {" " background-color: %1;" " border-top: 1px solid %2;" " border-bottom: 1px solid %2;" " color: %3;" "}" ; css = css .arg(bgColor.name()) .arg(borderColor.name()) .arg(fgColor.name()) ; mSaveBarWidget->setStyleSheet(css); } void applyFullScreenStyleSheet() { QString css = "#saveBarWidget {" " background-color: #333;" "}"; mSaveBarWidget->setStyleSheet(css); } void updateTooManyChangesFrame(const QList& list) { qreal maxPercentageOfMemoryUsage = GwenviewConfig::percentageOfMemoryUsageWarning(); qulonglong maxMemoryUsage = MemoryUtils::getTotalMemory() * maxPercentageOfMemoryUsage; qulonglong memoryUsage = 0; Q_FOREACH(const QUrl &url, list) { Document::Ptr doc = DocumentFactory::instance()->load(url); memoryUsage += doc->memoryUsage(); } mTooManyChangesFrame->setVisible(memoryUsage > maxMemoryUsage); } void updateTopRowWidget(const QList& lst) { QStringList links; QString message; if (lst.contains(mCurrentUrl)) { message = i18n("Current image modified"); mUndoButton->show(); mRedoButton->show(); if (lst.size() > 1) { QString previous = i18n("Previous modified image"); QString next = i18n("Next modified image"); if (mCurrentUrl == lst[0]) { links << previous; } else { - links << QString("%1").arg(previous); + links << QStringLiteral("%1").arg(previous); } if (mCurrentUrl == lst[lst.size() - 1]) { links << next; } else { - links << QString("%1").arg(next); + links << QStringLiteral("%1").arg(next); } } } else { mUndoButton->hide(); mRedoButton->hide(); message = i18np("One image modified", "%1 images modified", lst.size()); if (lst.size() > 1) { - links << QString("%1").arg(i18n("Go to first modified image")); + links << QStringLiteral("%1").arg(i18n("Go to first modified image")); } else { - links << QString("%1").arg(i18n("Go to it")); + links << QStringLiteral("%1").arg(i18n("Go to it")); } } mSaveCurrentUrlButton->setVisible(lst.contains(mCurrentUrl)); mSaveAsButton->setVisible(lst.contains(mCurrentUrl)); mSaveAllButton->setVisible(lst.size() >= 1); mMessageLabel->setText(message); mActionsLabel->setText(links.join(" | ")); } void updateWidgetSizes() { QVBoxLayout* layout = static_cast(mSaveBarWidget->layout()); int topRowHeight = q->window()->isFullScreen() ? 0 : mTopRowWidget->height(); int bottomRowHeight = mTooManyChangesFrame->isVisibleTo(mSaveBarWidget) ? mTooManyChangesFrame->sizeHint().height() : 0; int height = 2 * layout->margin() + topRowHeight + bottomRowHeight; if (topRowHeight > 0 && bottomRowHeight > 0) { height += layout->spacing(); } mSaveBarWidget->setFixedHeight(height); } }; SaveBar::SaveBar(QWidget* parent, KActionCollection* actionCollection) : SlideContainer(parent) , d(new SaveBarPrivate) { d->q = this; d->mActionCollection = actionCollection; d->mSaveBarWidget = new QWidget(); d->mSaveBarWidget->setObjectName(QLatin1String("saveBarWidget")); d->applyNormalStyleSheet(); d->mMessageLabel = new QLabel; d->mMessageLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); d->mUndoButton = createToolButton(); d->mRedoButton = createToolButton(); d->mSaveCurrentUrlButton = createToolButton(); d->mSaveAsButton = createToolButton(); d->mSaveAllButton = createToolButton(); d->mActionsLabel = new QLabel; d->mActionsLabel->setAlignment(Qt::AlignCenter); d->mActionsLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); d->createTooManyChangesFrame(); // Setup top row d->mTopRowWidget = new QWidget; QHBoxLayout* rowLayout = new QHBoxLayout(d->mTopRowWidget); rowLayout->addWidget(d->mMessageLabel); rowLayout->addWidget(d->mUndoButton); rowLayout->addWidget(d->mRedoButton); rowLayout->addWidget(d->mActionsLabel); rowLayout->addWidget(d->mSaveCurrentUrlButton); rowLayout->addWidget(d->mSaveAsButton); rowLayout->addWidget(d->mSaveAllButton); rowLayout->setMargin(0); // Setup bottom row QHBoxLayout* bottomRowLayout = new QHBoxLayout; bottomRowLayout->addStretch(); bottomRowLayout->addWidget(d->mTooManyChangesFrame); bottomRowLayout->addStretch(); // Gather everything together QVBoxLayout* layout = new QVBoxLayout(d->mSaveBarWidget); layout->addWidget(d->mTopRowWidget); layout->addLayout(bottomRowLayout); layout->setMargin(3); layout->setSpacing(3); setContent(d->mSaveBarWidget); connect(DocumentFactory::instance(), SIGNAL(modifiedDocumentListChanged()), SLOT(updateContent())); connect(d->mActionsLabel, &QLabel::linkActivated, this, &SaveBar::triggerAction); } SaveBar::~SaveBar() { delete d; } void SaveBar::initActionDependentWidgets() { d->mUndoButton->setDefaultAction(d->mActionCollection->action("edit_undo")); d->mRedoButton->setDefaultAction(d->mActionCollection->action("edit_redo")); d->mSaveCurrentUrlButton->setDefaultAction(d->mActionCollection->action("file_save")); d->mSaveAsButton->setDefaultAction(d->mActionCollection->action("file_save_as")); // FIXME: Not using an action for now d->mSaveAllButton->setText(i18n("Save All")); d->mSaveAllButton->setIcon(QIcon::fromTheme("document-save-all")); connect(d->mSaveAllButton, &QToolButton::clicked, this, &SaveBar::requestSaveAll); d->mSaveAllFullScreenButton->setText(i18n("Save All")); connect(d->mSaveAllFullScreenButton, &QToolButton::clicked, this, &SaveBar::requestSaveAll); int height = d->mUndoButton->sizeHint().height(); d->mTopRowWidget->setFixedHeight(height); d->updateWidgetSizes(); } void SaveBar::setFullScreenMode(bool isFullScreen) { d->mSaveAllFullScreenButton->setVisible(isFullScreen); if (isFullScreen) { d->applyFullScreenStyleSheet(); } else { d->applyNormalStyleSheet(); } updateContent(); } void SaveBar::updateContent() { QList lst = DocumentFactory::instance()->modifiedDocumentList(); if (window()->isFullScreen()) { d->mTopRowWidget->hide(); } else { d->mTopRowWidget->show(); d->updateTopRowWidget(lst); } d->updateTooManyChangesFrame(lst); d->updateWidgetSizes(); if (lst.isEmpty() || (window()->isFullScreen() && !d->mTooManyChangesFrame->isVisibleTo(d->mSaveBarWidget))) { slideOut(); } else { slideIn(); } } void SaveBar::triggerAction(const QString& action) { QList lst = DocumentFactory::instance()->modifiedDocumentList(); if (action == "first") { goToUrl(lst[0]); } else if (action == "previous") { int pos = lst.indexOf(d->mCurrentUrl); --pos; Q_ASSERT(pos >= 0); goToUrl(lst[pos]); } else if (action == "next") { int pos = lst.indexOf(d->mCurrentUrl); ++pos; Q_ASSERT(pos < lst.size()); goToUrl(lst[pos]); } else { qWarning() << "Unknown action: " << action ; } } void SaveBar::setCurrentUrl(const QUrl &url) { d->mCurrentUrl = url; updateContent(); } } // namespace diff --git a/app/semanticinfocontextmanageritem.cpp b/app/semanticinfocontextmanageritem.cpp index 7ac23336..23aa9f32 100644 --- a/app/semanticinfocontextmanageritem.cpp +++ b/app/semanticinfocontextmanageritem.cpp @@ -1,472 +1,472 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 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, Cambridge, MA 02110-1301, USA. */ // Self #include "semanticinfocontextmanageritem.h" // Qt #include #include #include #include #include #include #include #include // KDE #include #include #include #include #include #include #include #include // Local #include "viewmainpage.h" #include "sidebar.h" #include "ui_semanticinfosidebaritem.h" #include "ui_semanticinfodialog.h" #include #include #include #include #include #include #include #include #include namespace Gwenview { static const int RATING_INDICATOR_HIDE_DELAY = 2000; struct SemanticInfoDialog : public QDialog, public Ui_SemanticInfoDialog { SemanticInfoDialog(QWidget* parent) : QDialog(parent) { setLayout(new QVBoxLayout); QWidget* mainWidget = new QWidget; layout()->addWidget(mainWidget); setupUi(mainWidget); mainWidget->layout()->setMargin(0); setWindowTitle(mainWidget->windowTitle()); KWindowConfig::restoreWindowSize(windowHandle(), configGroup()); } ~SemanticInfoDialog() { KConfigGroup group = configGroup(); KWindowConfig::saveWindowSize(windowHandle(), group); } KConfigGroup configGroup() const { KSharedConfigPtr config = KSharedConfig::openConfig(); return KConfigGroup(config, "SemanticInfoDialog"); } }; /** * A QGraphicsPixmapItem-like class, but which inherits from QGraphicsWidget */ class GraphicsPixmapWidget : public QGraphicsWidget { public: void setPixmap(const QPixmap& pix) { mPix = pix; setMinimumSize(pix.size()); } - void paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*) Q_DECL_OVERRIDE + void paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*) override { painter->drawPixmap( (size().width() - mPix.width()) / 2, (size().height() - mPix.height()) / 2, mPix); } private: QPixmap mPix; }; class RatingIndicator : public HudWidget { public: RatingIndicator() : HudWidget() , mPixmapWidget(new GraphicsPixmapWidget) , mDeleteTimer(new QTimer(this)) { updatePixmap(0); setOpacity(0); init(mPixmapWidget, OptionNone); mDeleteTimer->setInterval(RATING_INDICATOR_HIDE_DELAY); mDeleteTimer->setSingleShot(true); connect(mDeleteTimer, SIGNAL(timeout()), SLOT(fadeOut())); connect(this, SIGNAL(fadedOut()), SLOT(deleteLater())); } void setRating(int rating) { updatePixmap(rating); update(); mDeleteTimer->start(); fadeIn(); } private: GraphicsPixmapWidget* mPixmapWidget; QTimer* mDeleteTimer; void updatePixmap(int rating) { KRatingPainter ratingPainter; const int iconSize = KIconLoader::global()->currentSize(KIconLoader::Small); QPixmap pix(iconSize * 5 + ratingPainter.spacing() * 4, iconSize); pix.fill(Qt::transparent); { QPainter painter(&pix); ratingPainter.paint(&painter, pix.rect(), rating); } mPixmapWidget->setPixmap(pix); } }; struct SemanticInfoContextManagerItemPrivate : public Ui_SemanticInfoSideBarItem { SemanticInfoContextManagerItem* q; SideBarGroup* mGroup; KActionCollection* mActionCollection; ViewMainPage* mViewMainPage; QPointer mSemanticInfoDialog; TagInfo mTagInfo; QAction * mEditTagsAction; QSignalMapper* mRatingMapper; /** A list of all actions, so that we can disable them when necessary */ QList mActions; QPointer mRatingIndicator; void setupGroup() { mGroup = new SideBarGroup(i18n("Semantic Information"), false); q->setWidget(mGroup); EventWatcher::install(mGroup, QEvent::Show, q, SLOT(update())); QWidget* container = new QWidget; setupUi(container); container->layout()->setMargin(0); mGroup->addWidget(container); formLayout->setContentsMargins(DEFAULT_LAYOUT_MARGIN, 0, 0, 0); QObject::connect(mRatingWidget, SIGNAL(ratingChanged(int)), q, SLOT(slotRatingChanged(int))); QObject::connect(mRatingMapper, SIGNAL(mapped(int)), mRatingWidget, SLOT(setRating(int))); mDescriptionTextEdit->installEventFilter(q); QObject::connect(mTagLabel, SIGNAL(linkActivated(QString)), mEditTagsAction, SLOT(trigger())); } void setupActions() { KActionCategory* edit = new KActionCategory(i18nc("@title actions category", "Edit"), mActionCollection); mEditTagsAction = edit->addAction("edit_tags"); mEditTagsAction->setText(i18nc("@action", "Edit Tags")); mEditTagsAction->setIcon(QIcon::fromTheme("tag")); mActionCollection->setDefaultShortcut(mEditTagsAction, Qt::CTRL + Qt::Key_T); QObject::connect(mEditTagsAction, SIGNAL(triggered()), q, SLOT(showSemanticInfoDialog())); mActions << mEditTagsAction; mRatingMapper = new QSignalMapper(q); for (int rating = 0; rating <= 5; ++rating) { - QAction * action = edit->addAction(QString("rate_%1").arg(rating)); + QAction * action = edit->addAction(QStringLiteral("rate_%1").arg(rating)); if (rating == 0) { action->setText(i18nc("@action Rating value of zero", "Zero")); } else { action->setText(QString(rating, QChar(0x22C6))); /* 0x22C6 is the 'star' character */ } mActionCollection->setDefaultShortcut(action, Qt::Key_0 + rating); QObject::connect(action, SIGNAL(triggered()), mRatingMapper, SLOT(map())); mRatingMapper->setMapping(action, rating * 2); mActions << action; } QObject::connect(mRatingMapper, SIGNAL(mapped(int)), q, SLOT(slotRatingChanged(int))); } void updateTagLabel() { if (q->contextManager()->selectedFileItemList().isEmpty()) { mTagLabel->clear(); return; } AbstractSemanticInfoBackEnd* backEnd = q->contextManager()->dirModel()->semanticInfoBackEnd(); TagInfo::ConstIterator it = mTagInfo.constBegin(), end = mTagInfo.constEnd(); QMap labelMap; for (; it != end; ++it) { SemanticInfoTag tag = it.key(); QString label = backEnd->labelForTag(tag); if (!it.value()) { // Tag is not present for all urls label += '*'; } labelMap[label.toLower()] = label; } QStringList labels(labelMap.values()); QString editLink = i18n("Edit"); - QString text = labels.join(", ") + QString(" %1").arg(editLink); + QString text = labels.join(", ") + QStringLiteral(" %1").arg(editLink); mTagLabel->setText(text); } void updateSemanticInfoDialog() { mSemanticInfoDialog->mTagWidget->setEnabled(!q->contextManager()->selectedFileItemList().isEmpty()); mSemanticInfoDialog->mTagWidget->setTagInfo(mTagInfo); } }; SemanticInfoContextManagerItem::SemanticInfoContextManagerItem(ContextManager* manager, KActionCollection* actionCollection, ViewMainPage* viewMainPage) : AbstractContextManagerItem(manager) , d(new SemanticInfoContextManagerItemPrivate) { d->q = this; d->mActionCollection = actionCollection; d->mViewMainPage = viewMainPage; connect(contextManager(), SIGNAL(selectionChanged()), SLOT(slotSelectionChanged())); connect(contextManager(), SIGNAL(selectionDataChanged()), SLOT(update())); connect(contextManager(), SIGNAL(currentDirUrlChanged(QUrl)), SLOT(update())); d->setupActions(); d->setupGroup(); } SemanticInfoContextManagerItem::~SemanticInfoContextManagerItem() { delete d; } inline int ratingForVariant(const QVariant& variant) { if (variant.isValid()) { return variant.toInt(); } else { return 0; } } void SemanticInfoContextManagerItem::slotSelectionChanged() { update(); } void SemanticInfoContextManagerItem::update() { KFileItemList itemList = contextManager()->selectedFileItemList(); bool first = true; int rating = 0; QString description; SortedDirModel* dirModel = contextManager()->dirModel(); // This hash stores for how many items the tag is present // If you have 3 items, and only 2 have the "Holiday" tag, // then tagHash["Holiday"] will be 2 at the end of the loop. typedef QHash TagHash; TagHash tagHash; Q_FOREACH(const KFileItem & item, itemList) { QModelIndex index = dirModel->indexForItem(item); QVariant value = dirModel->data(index, SemanticInfoDirModel::RatingRole); if (first) { rating = ratingForVariant(value); } else if (rating != ratingForVariant(value)) { // Ratings aren't the same, reset rating = 0; } QString indexDescription = index.data(SemanticInfoDirModel::DescriptionRole).toString(); if (first) { description = indexDescription; } else if (description != indexDescription) { description.clear(); } // Fill tagHash, incrementing the tag count if it's already there TagSet tagSet = TagSet::fromVariant(index.data(SemanticInfoDirModel::TagsRole)); Q_FOREACH(const QString & tag, tagSet) { TagHash::Iterator it = tagHash.find(tag); if (it == tagHash.end()) { tagHash[tag] = 1; } else { ++it.value(); } } first = false; } { SignalBlocker blocker(d->mRatingWidget); d->mRatingWidget->setRating(rating); } d->mDescriptionTextEdit->setText(description); // Init tagInfo from tagHash d->mTagInfo.clear(); int itemCount = itemList.count(); TagHash::ConstIterator it = tagHash.constBegin(), end = tagHash.constEnd(); for (; it != end; ++it) { QString tag = it.key(); int count = it.value(); d->mTagInfo[tag] = count == itemCount; } bool enabled = !contextManager()->selectedFileItemList().isEmpty(); Q_FOREACH(QAction * action, d->mActions) { action->setEnabled(enabled); } d->updateTagLabel(); if (d->mSemanticInfoDialog) { d->updateSemanticInfoDialog(); } } void SemanticInfoContextManagerItem::slotRatingChanged(int rating) { KFileItemList itemList = contextManager()->selectedFileItemList(); // Show rating indicator in view mode, and only if sidebar is not visible if (d->mViewMainPage->isVisible() && !d->mRatingWidget->isVisible()) { if (!d->mRatingIndicator.data()) { d->mRatingIndicator = new RatingIndicator; d->mViewMainPage->showMessageWidget(d->mRatingIndicator, Qt::AlignBottom | Qt::AlignHCenter); } d->mRatingIndicator->setRating(rating); } SortedDirModel* dirModel = contextManager()->dirModel(); Q_FOREACH(const KFileItem & item, itemList) { QModelIndex index = dirModel->indexForItem(item); dirModel->setData(index, rating, SemanticInfoDirModel::RatingRole); } } void SemanticInfoContextManagerItem::storeDescription() { if (!d->mDescriptionTextEdit->document()->isModified()) { return; } d->mDescriptionTextEdit->document()->setModified(false); QString description = d->mDescriptionTextEdit->toPlainText(); KFileItemList itemList = contextManager()->selectedFileItemList(); SortedDirModel* dirModel = contextManager()->dirModel(); Q_FOREACH(const KFileItem & item, itemList) { QModelIndex index = dirModel->indexForItem(item); dirModel->setData(index, description, SemanticInfoDirModel::DescriptionRole); } } void SemanticInfoContextManagerItem::assignTag(const SemanticInfoTag& tag) { KFileItemList itemList = contextManager()->selectedFileItemList(); SortedDirModel* dirModel = contextManager()->dirModel(); Q_FOREACH(const KFileItem & item, itemList) { QModelIndex index = dirModel->indexForItem(item); TagSet tags = TagSet::fromVariant(dirModel->data(index, SemanticInfoDirModel::TagsRole)); if (!tags.contains(tag)) { tags << tag; dirModel->setData(index, tags.toVariant(), SemanticInfoDirModel::TagsRole); } } } void SemanticInfoContextManagerItem::removeTag(const SemanticInfoTag& tag) { KFileItemList itemList = contextManager()->selectedFileItemList(); SortedDirModel* dirModel = contextManager()->dirModel(); Q_FOREACH(const KFileItem & item, itemList) { QModelIndex index = dirModel->indexForItem(item); TagSet tags = TagSet::fromVariant(dirModel->data(index, SemanticInfoDirModel::TagsRole)); if (tags.contains(tag)) { tags.remove(tag); dirModel->setData(index, tags.toVariant(), SemanticInfoDirModel::TagsRole); } } } void SemanticInfoContextManagerItem::showSemanticInfoDialog() { if (!d->mSemanticInfoDialog) { d->mSemanticInfoDialog = new SemanticInfoDialog(d->mGroup); d->mSemanticInfoDialog->setAttribute(Qt::WA_DeleteOnClose, true); connect(d->mSemanticInfoDialog->mPreviousButton, SIGNAL(clicked()), d->mActionCollection->action("go_previous"), SLOT(trigger())); connect(d->mSemanticInfoDialog->mNextButton, SIGNAL(clicked()), d->mActionCollection->action("go_next"), SLOT(trigger())); connect(d->mSemanticInfoDialog->mButtonBox, SIGNAL(rejected()), d->mSemanticInfoDialog, SLOT(close())); AbstractSemanticInfoBackEnd* backEnd = contextManager()->dirModel()->semanticInfoBackEnd(); d->mSemanticInfoDialog->mTagWidget->setSemanticInfoBackEnd(backEnd); connect(d->mSemanticInfoDialog->mTagWidget, SIGNAL(tagAssigned(SemanticInfoTag)), SLOT(assignTag(SemanticInfoTag))); connect(d->mSemanticInfoDialog->mTagWidget, SIGNAL(tagRemoved(SemanticInfoTag)), SLOT(removeTag(SemanticInfoTag))); } d->updateSemanticInfoDialog(); d->mSemanticInfoDialog->show(); } bool SemanticInfoContextManagerItem::eventFilter(QObject*, QEvent* event) { if (event->type() == QEvent::FocusOut) { storeDescription(); } return false; } } // namespace diff --git a/app/semanticinfocontextmanageritem.h b/app/semanticinfocontextmanageritem.h index d9bdab60..0758bdd8 100644 --- a/app/semanticinfocontextmanageritem.h +++ b/app/semanticinfocontextmanageritem.h @@ -1,66 +1,66 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 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, Cambridge, MA 02110-1301, USA. */ #ifndef SEMANTICINFOCONTEXTMANAGERITEM_H #define SEMANTICINFOCONTEXTMANAGERITEM_H // Qt // KDE // Local #include "abstractcontextmanageritem.h" #include class KActionCollection; namespace Gwenview { class ViewMainPage; struct SemanticInfoContextManagerItemPrivate; class SemanticInfoContextManagerItem : public AbstractContextManagerItem { Q_OBJECT public: SemanticInfoContextManagerItem(ContextManager*, KActionCollection*, ViewMainPage* viewMainPage); - ~SemanticInfoContextManagerItem() Q_DECL_OVERRIDE; + ~SemanticInfoContextManagerItem() override; protected: - bool eventFilter(QObject*, QEvent*) Q_DECL_OVERRIDE; + bool eventFilter(QObject*, QEvent*) override; private Q_SLOTS: void slotSelectionChanged(); void update(); void slotRatingChanged(int rating); void storeDescription(); void assignTag(const SemanticInfoTag&); void removeTag(const SemanticInfoTag&); void showSemanticInfoDialog(); private: friend struct SemanticInfoContextManagerItemPrivate; SemanticInfoContextManagerItemPrivate* const d; }; } // namespace #endif /* SEMANTICINFOCONTEXTMANAGERITEM_H */ diff --git a/app/sidebar.cpp b/app/sidebar.cpp index 24bc16f0..73950e85 100644 --- a/app/sidebar.cpp +++ b/app/sidebar.cpp @@ -1,265 +1,265 @@ /* 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. */ #include "sidebar.h" // Qt #include #include #include #include #include #include #include #include // KDE #include #include // Local #include #include namespace Gwenview { /** * A button which always leave room for an icon, even if there is none, so that * all button texts are correctly aligned. */ class SideBarButton : public QToolButton { protected: - void paintEvent(QPaintEvent* event) Q_DECL_OVERRIDE + void paintEvent(QPaintEvent* event) override { forceIcon(); QToolButton::paintEvent(event); } - QSize sizeHint() const Q_DECL_OVERRIDE + QSize sizeHint() const override { const_cast(this)->forceIcon(); return QToolButton::sizeHint(); } private: void forceIcon() { if (!icon().isNull()) { return; } // Assign an empty icon to the button if there is no icon associated // with the action so that all button texts are correctly aligned. QSize wantedSize = iconSize(); if (mEmptyIcon.isNull() || mEmptyIcon.actualSize(wantedSize) != wantedSize) { QPixmap pix(wantedSize); pix.fill(Qt::transparent); mEmptyIcon.addPixmap(pix); } setIcon(mEmptyIcon); } QIcon mEmptyIcon; }; //- SideBarGroup --------------------------------------------------------------- struct SideBarGroupPrivate { QFrame* mContainer; QLabel* mTitleLabel; bool mDefaultContainerMarginEnabled; }; SideBarGroup::SideBarGroup(const QString& title, bool defaultContainerMarginEnabled) : QFrame() , d(new SideBarGroupPrivate) { d->mContainer = nullptr; d->mTitleLabel = new QLabel(this); d->mDefaultContainerMarginEnabled = defaultContainerMarginEnabled; d->mTitleLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); d->mTitleLabel->setFixedHeight(d->mTitleLabel->sizeHint().height() * 3 / 2); QFont font(d->mTitleLabel->font()); font.setBold(true); d->mTitleLabel->setFont(font); d->mTitleLabel->setText(title); QVBoxLayout* layout = new QVBoxLayout(this); layout->setMargin(0); layout->setSpacing(0); layout->addWidget(d->mTitleLabel); d->mTitleLabel->setContentsMargins(DEFAULT_LAYOUT_MARGIN, 0, 0, 0); clear(); } SideBarGroup::~SideBarGroup() { delete d; } void SideBarGroup::paintEvent(QPaintEvent* event) { QFrame::paintEvent(event); if (parentWidget()->layout()->indexOf(this) != 0) { // Draw a separator, but only if we are not the first group QPainter painter(this); QPen pen(palette().mid().color()); painter.setPen(pen); painter.drawLine(rect().topLeft(), rect().topRight()); } } void SideBarGroup::addWidget(QWidget* widget) { widget->setParent(d->mContainer); d->mContainer->layout()->addWidget(widget); } void SideBarGroup::clear() { if (d->mContainer) { d->mContainer->deleteLater(); } d->mContainer = new QFrame(this); QVBoxLayout* containerLayout = new QVBoxLayout(d->mContainer); containerLayout->setMargin(0); containerLayout->setSpacing(0); layout()->addWidget(d->mContainer); if(d->mDefaultContainerMarginEnabled) { d->mContainer->layout()->setContentsMargins(DEFAULT_LAYOUT_MARGIN, 0, 0, 0); } } void SideBarGroup::addAction(QAction* action) { int size = KIconLoader::global()->currentSize(KIconLoader::Small); QToolButton* button = new SideBarButton(); button->setFocusPolicy(Qt::NoFocus); button->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); button->setAutoRaise(true); button->setDefaultAction(action); button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); button->setIconSize(QSize(size, size)); if (action->menu()) { button->setPopupMode(QToolButton::InstantPopup); } addWidget(button); } //- SideBarPage ---------------------------------------------------------------- struct SideBarPagePrivate { QString mTitle; QVBoxLayout* mLayout; }; SideBarPage::SideBarPage(const QString& title) : QWidget() , d(new SideBarPagePrivate) { d->mTitle = title; d->mLayout = new QVBoxLayout(this); d->mLayout->setMargin(0); } SideBarPage::~SideBarPage() { delete d; } const QString& SideBarPage::title() const { return d->mTitle; } void SideBarPage::addWidget(QWidget* widget) { d->mLayout->addWidget(widget); } void SideBarPage::addStretch() { d->mLayout->addStretch(); } //- SideBar -------------------------------------------------------------------- struct SideBarPrivate { }; SideBar::SideBar(QWidget* parent) : QTabWidget(parent) , d(new SideBarPrivate) { setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont)); tabBar()->setDocumentMode(true); tabBar()->setUsesScrollButtons(false); tabBar()->setFocusPolicy(Qt::NoFocus); setTabPosition(QTabWidget::South); setElideMode(Qt::ElideRight); connect(tabBar(), &QTabBar::currentChanged, [=]() { GwenviewConfig::setSideBarPage(currentPage()); }); } SideBar::~SideBar() { delete d; } QSize SideBar::sizeHint() const { return QSize(200, 200); } void SideBar::addPage(SideBarPage* page) { // Prevent emitting currentChanged() while populating pages SignalBlocker blocker(tabBar()); addTab(page, page->title()); } QString SideBar::currentPage() const { return currentWidget()->objectName(); } void SideBar::setCurrentPage(const QString& name) { for (int index = 0; index < count(); ++index) { if (widget(index)->objectName() == name) { setCurrentIndex(index); } } } void SideBar::loadConfig() { setCurrentPage(GwenviewConfig::sideBarPage()); } } // namespace diff --git a/app/sidebar.h b/app/sidebar.h index fc8de5b7..757f62b7 100644 --- a/app/sidebar.h +++ b/app/sidebar.h @@ -1,92 +1,92 @@ /* 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. */ #ifndef SIDEBAR_H #define SIDEBAR_H // Qt #include #include namespace Gwenview { static const int DEFAULT_LAYOUT_MARGIN = 6; class SideBar; struct SideBarGroupPrivate; class SideBarGroup : public QFrame { Q_OBJECT public: SideBarGroup(const QString& title, bool defaultContainerMarginEnabled = true); - ~SideBarGroup() Q_DECL_OVERRIDE; + ~SideBarGroup() override; void addWidget(QWidget*); void addAction(QAction*); void clear(); protected: - void paintEvent(QPaintEvent*) Q_DECL_OVERRIDE; + void paintEvent(QPaintEvent*) override; private: SideBarGroupPrivate* const d; }; struct SideBarPagePrivate; class SideBarPage : public QWidget { Q_OBJECT public: SideBarPage(const QString& title); ~SideBarPage(); void addWidget(QWidget*); void addStretch(); const QString& title() const; private: SideBarPagePrivate* const d; }; struct SideBarPrivate; class SideBar : public QTabWidget { Q_OBJECT public: explicit SideBar(QWidget* parent); - ~SideBar() Q_DECL_OVERRIDE; + ~SideBar() override; void addPage(SideBarPage*); QString currentPage() const; void setCurrentPage(const QString& name); void loadConfig(); - QSize sizeHint() const Q_DECL_OVERRIDE; + QSize sizeHint() const override; private: SideBarPrivate* const d; }; } // namespace #endif /* SIDEBAR_H */ diff --git a/app/splitter.h b/app/splitter.h index c3ff0b80..2772be78 100644 --- a/app/splitter.h +++ b/app/splitter.h @@ -1,76 +1,76 @@ /* Gwenview: an image viewer Copyright 2009 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. */ #ifndef SPLITTER_H #define SPLITTER_H // Qt #include #include namespace Gwenview { class SplitterHandle : public QSplitterHandle { public: SplitterHandle(Qt::Orientation orientation, QSplitter* parent) : QSplitterHandle(orientation, parent) {} protected: - void paintEvent(QPaintEvent* event) Q_DECL_OVERRIDE + void paintEvent(QPaintEvent* event) override { QSplitterHandle::paintEvent(event); QPainter painter(this); painter.setPen(palette().mid().color()); if (orientation() == Qt::Vertical) { painter.drawLine(rect().topLeft(), rect().topRight()); painter.drawLine(rect().bottomLeft(), rect().bottomRight()); } else { //painter.drawLine(rect().topLeft(), rect().bottomLeft()); painter.drawLine(rect().topRight(), rect().bottomRight()); } } }; /** * Home made splitter to be able to define a custom handle which is border with * "mid" colored lines. */ class Splitter : public QSplitter { public: Splitter(Qt::Orientation orientation, QWidget* parent) : QSplitter(orientation, parent) { setHandleWidth(handleWidth() + 2); } protected: - QSplitterHandle* createHandle() Q_DECL_OVERRIDE + QSplitterHandle* createHandle() override { return new SplitterHandle(orientation(), this); } }; } // namespace #endif /* SPLITTER_H */ diff --git a/app/startmainpage.cpp b/app/startmainpage.cpp index bb711395..95d385e0 100644 --- a/app/startmainpage.cpp +++ b/app/startmainpage.cpp @@ -1,335 +1,335 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 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, Cambridge, MA 02110-1301, USA. */ // Self #include "startmainpage.h" #include // Qt #include #include #ifdef GTK_WORKAROUND_BROKE_IN_KF5_PORT #include #endif #include #include // KDE #include #include // Local #include #include #include #include #include #include #include #include #include #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE #include #endif namespace Gwenview { class HistoryThumbnailViewHelper : public AbstractThumbnailViewHelper { public: HistoryThumbnailViewHelper(QObject* parent) : AbstractThumbnailViewHelper(parent) {} - void showContextMenu(QWidget*) Q_DECL_OVERRIDE + void showContextMenu(QWidget*) override { } - void showMenuForUrlDroppedOnViewport(QWidget*, const QList&) Q_DECL_OVERRIDE + void showMenuForUrlDroppedOnViewport(QWidget*, const QList&) override { } - void showMenuForUrlDroppedOnDir(QWidget*, const QList&, const QUrl&) Q_DECL_OVERRIDE + void showMenuForUrlDroppedOnDir(QWidget*, const QList&, const QUrl&) override { } }; struct StartMainPagePrivate : public Ui_StartMainPage { StartMainPage* q; GvCore* mGvCore; KFilePlacesModel* mBookmarksModel; ThumbnailProvider *mRecentFilesThumbnailProvider; bool mSearchUiInitialized; void setupSearchUi() { #ifdef GWENVIEW_SEMANTICINFO_BACKEND_BALOO mTagView->setModel(TagModel::createAllTagsModel(mTagView, mGvCore->semanticInfoBackEnd())); mTagView->show(); mTagLabel->hide(); #else mTagView->hide(); mTagLabel->hide(); #endif } void updateHistoryTab() { mHistoryWidget->setVisible(GwenviewConfig::historyEnabled()); mHistoryDisabledLabel->setVisible(!GwenviewConfig::historyEnabled()); } void setupHistoryView(ThumbnailView *view) { view->setThumbnailViewHelper(new HistoryThumbnailViewHelper(view)); PreviewItemDelegate* delegate = new PreviewItemDelegate(view); delegate->setContextBarActions(PreviewItemDelegate::NoAction); delegate->setTextElideMode(Qt::ElideLeft); view->setItemDelegate(delegate); view->setThumbnailWidth(128); view->setCreateThumbnailsForRemoteUrls(false); QModelIndex index = view->model()->index(0, 0); if (index.isValid()) { view->setCurrentIndex(index); } } }; static void initViewPalette(QAbstractItemView* view, const QColor& fgColor) { QWidget* viewport = view->viewport(); QPalette palette = viewport->palette(); palette.setColor(viewport->backgroundRole(), Qt::transparent); palette.setColor(QPalette::WindowText, fgColor); palette.setColor(QPalette::Text, fgColor); // QListView uses QStyledItemDelegate, which uses the view palette for // foreground color, while KFilePlacesView uses the viewport palette. viewport->setPalette(palette); view->setPalette(palette); } static bool styleIsGtkBased() { const char* name = QApplication::style()->metaObject()->className(); return qstrcmp(name, "QGtkStyle") == 0; } StartMainPage::StartMainPage(QWidget* parent, GvCore* gvCore) : QFrame(parent) , d(new StartMainPagePrivate) { d->mRecentFilesThumbnailProvider = nullptr; d->q = this; d->mGvCore = gvCore; d->mSearchUiInitialized = false; d->setupUi(this); if (styleIsGtkBased()) { #ifdef GTK_WORKAROUND_BROKE_IN_KF5_PORT // Gtk-based styles do not apply the correct background color on tabs. // As a workaround, use the Plastique style instead. QStyle* fix = new QPlastiqueStyle(); fix->setParent(this); d->mHistoryWidget->tabBar()->setStyle(fix); d->mPlacesTagsWidget->tabBar()->setStyle(fix); #endif } setFrameStyle(QFrame::NoFrame); // Bookmark view d->mBookmarksModel = new KFilePlacesModel(this); d->mBookmarksView->setModel(d->mBookmarksModel); d->mBookmarksView->setAutoResizeItemsEnabled(false); connect(d->mBookmarksView, &KFilePlacesView::urlChanged, this, &StartMainPage::urlSelected); // Tag view connect(d->mTagView, &QListView::clicked, this, &StartMainPage::slotTagViewClicked); // Recent folders view connect(d->mRecentFoldersView, &Gwenview::ThumbnailView::indexActivated, this, &StartMainPage::slotListViewActivated); connect(d->mRecentFoldersView, &Gwenview::ThumbnailView::customContextMenuRequested, this, &StartMainPage::showContextMenu); // Recent files view connect(d->mRecentFilesView, &Gwenview::ThumbnailView::indexActivated, this, &StartMainPage::slotListViewActivated); connect(d->mRecentFilesView, &Gwenview::ThumbnailView::customContextMenuRequested, this, &StartMainPage::showContextMenu); d->updateHistoryTab(); connect(GwenviewConfig::self(), &GwenviewConfig::configChanged, this, &StartMainPage::loadConfig); d->mRecentFoldersView->setFocus(); } StartMainPage::~StartMainPage() { delete d->mRecentFilesThumbnailProvider; delete d; } void StartMainPage::slotTagViewClicked(const QModelIndex& index) { #ifdef GWENVIEW_SEMANTICINFO_BACKEND_BALOO if (!index.isValid()) { return; } // FIXME: Check label encoding const QString tag = index.data().toString(); emit urlSelected(QUrl("tags:/" + tag)); #endif } void StartMainPage::applyPalette(const QPalette& newPalette) { QColor fgColor = newPalette.text().color(); QPalette pal = palette(); pal.setBrush(backgroundRole(), newPalette.base()); pal.setBrush(QPalette::Button, newPalette.base()); pal.setBrush(QPalette::WindowText, fgColor); pal.setBrush(QPalette::ButtonText, fgColor); pal.setBrush(QPalette::Text, fgColor); setPalette(pal); initViewPalette(d->mBookmarksView, fgColor); initViewPalette(d->mTagView, fgColor); initViewPalette(d->mRecentFoldersView, fgColor); initViewPalette(d->mRecentFilesView, fgColor); } void StartMainPage::slotListViewActivated(const QModelIndex& index) { if (!index.isValid()) { return; } QVariant data = index.data(KFilePlacesModel::UrlRole); QUrl url = data.toUrl(); // Prevent dir lister error if (!url.isValid()) { qCritical() << "Tried to open an invalid url"; return; } emit urlSelected(url); } void StartMainPage::showEvent(QShowEvent* event) { if (GwenviewConfig::historyEnabled()) { if (!d->mRecentFoldersView->model()) { d->mRecentFoldersView->setModel(d->mGvCore->recentFoldersModel()); d->setupHistoryView(d->mRecentFoldersView); } if (!d->mRecentFilesView->model()) { d->mRecentFilesView->setModel(d->mGvCore->recentFilesModel()); d->mRecentFilesThumbnailProvider = new ThumbnailProvider(); d->mRecentFilesView->setThumbnailProvider(d->mRecentFilesThumbnailProvider); d->setupHistoryView(d->mRecentFilesView); } } if (!d->mSearchUiInitialized) { d->mSearchUiInitialized = true; d->setupSearchUi(); } QFrame::showEvent(event); } void StartMainPage::showContextMenu(const QPoint& pos) { // Create menu DialogGuard menu(this); QAction* addAction = menu->addAction(QIcon::fromTheme("bookmark-new"), QString()); QAction* forgetAction = menu->addAction(QIcon::fromTheme("edit-delete"), QString()); menu->addSeparator(); QAction* forgetAllAction = menu->addAction(QIcon::fromTheme("edit-delete-all"), QString()); if (d->mHistoryWidget->currentWidget() == d->mRecentFoldersTab) { addAction->setText(i18nc("@action Recent Folders view", "Add Folder to Places")); forgetAction->setText(i18nc("@action Recent Folders view", "Forget This Folder")); forgetAllAction->setText(i18nc("@action Recent Folders view", "Forget All Folders")); } else if (d->mHistoryWidget->currentWidget() == d->mRecentFilesTab) { addAction->setText(i18nc("@action Recent Files view", "Add Containing Folder to Places")); forgetAction->setText(i18nc("@action Recent Files view", "Forget This File")); forgetAllAction->setText(i18nc("@action Recent Files view", "Forget All Files")); } else { GV_WARN_AND_RETURN("Context menu not implemented on this tab page"); } const QAbstractItemView* view = qobject_cast(sender()); const QModelIndex index = view->indexAt(pos); addAction->setEnabled(index.isValid()); forgetAction->setEnabled(index.isValid()); // Handle menu const QAction* action = menu->exec(view->mapToGlobal(pos)); if (!action) { return; } const QVariant data = index.data(KFilePlacesModel::UrlRole); QUrl url = data.toUrl(); if (action == addAction) { if (d->mHistoryWidget->currentWidget() == d->mRecentFilesTab) { url = url.adjusted(QUrl::RemoveFilename); } QString text = url.adjusted(QUrl::StripTrailingSlash).fileName(); if (text.isEmpty()) { text = url.toDisplayString(); } d->mBookmarksModel->addPlace(text, url); } else if (action == forgetAction) { view->model()->removeRow(index.row()); if (d->mHistoryWidget->currentWidget() == d->mRecentFilesTab) { emit recentFileRemoved(url); } } else if (action == forgetAllAction) { view->model()->removeRows(0, view->model()->rowCount()); if (d->mHistoryWidget->currentWidget() == d->mRecentFilesTab) { emit recentFilesCleared(); } } } void StartMainPage::loadConfig() { d->updateHistoryTab(); applyPalette(d->mGvCore->palette(GvCore::NormalViewPalette)); } ThumbnailView* StartMainPage::recentFoldersView() const { return d->mRecentFoldersView; } } // namespace diff --git a/app/startmainpage.h b/app/startmainpage.h index 9f9e4c33..765e927e 100644 --- a/app/startmainpage.h +++ b/app/startmainpage.h @@ -1,77 +1,77 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 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, Cambridge, MA 02110-1301, USA. */ #ifndef STARTMAINPAGE_H #define STARTMAINPAGE_H // Qt #include // KDE // Local class QModelIndex; class QPalette; class QShowEvent; class QUrl; namespace Gwenview { class GvCore; class ThumbnailView; struct StartMainPagePrivate; class StartMainPage : public QFrame { Q_OBJECT public: explicit StartMainPage(QWidget* parent, GvCore*); - ~StartMainPage() Q_DECL_OVERRIDE; + ~StartMainPage() override; void applyPalette(const QPalette&); ThumbnailView* recentFoldersView() const; Q_SIGNALS: void urlSelected(const QUrl &url); void recentFileRemoved(const QUrl& url); void recentFilesCleared(); public Q_SLOTS: void loadConfig(); protected: - void showEvent(QShowEvent*) Q_DECL_OVERRIDE; + void showEvent(QShowEvent*) override; private Q_SLOTS: void slotListViewActivated(const QModelIndex& index); void slotTagViewClicked(const QModelIndex& index); private: StartMainPagePrivate* const d; void showContextMenu(const QPoint& pos); }; } // namespace #endif /* STARTMAINPAGE_H */ diff --git a/app/thumbnailviewhelper.h b/app/thumbnailviewhelper.h index cad73fad..20e255ca 100644 --- a/app/thumbnailviewhelper.h +++ b/app/thumbnailviewhelper.h @@ -1,60 +1,60 @@ // 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. */ #ifndef THUMBNAILVIEWHELPER_H #define THUMBNAILVIEWHELPER_H // Qt // KDE // Local #include class KActionCollection; namespace Gwenview { struct ThumbnailViewHelperPrivate; class ThumbnailViewHelper : public AbstractThumbnailViewHelper { Q_OBJECT public: ThumbnailViewHelper(QObject* parent, KActionCollection*); - ~ThumbnailViewHelper() Q_DECL_OVERRIDE; + ~ThumbnailViewHelper() override; - void showContextMenu(QWidget* parent) Q_DECL_OVERRIDE; + void showContextMenu(QWidget* parent) override; - void showMenuForUrlDroppedOnViewport(QWidget* parent, const QList&) Q_DECL_OVERRIDE; + void showMenuForUrlDroppedOnViewport(QWidget* parent, const QList&) override; - void showMenuForUrlDroppedOnDir(QWidget* parent, const QList&, const QUrl&) Q_DECL_OVERRIDE; + void showMenuForUrlDroppedOnDir(QWidget* parent, const QList&, const QUrl&) override; public Q_SLOTS: void setCurrentDirUrl(const QUrl&); private: ThumbnailViewHelperPrivate* const d; }; } // namespace #endif /* THUMBNAILVIEWHELPER_H */ diff --git a/app/viewmainpage.cpp b/app/viewmainpage.cpp index f6ccd695..fa06e80f 100644 --- a/app/viewmainpage.cpp +++ b/app/viewmainpage.cpp @@ -1,859 +1,859 @@ /* 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. */ #include "viewmainpage.h" // Qt #include #include #include #include #include #include #include // KDE #include #include #include #include #include #include #include #include // Local #include "fileoperations.h" #include #include "splitter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Gwenview { #undef ENABLE_LOG #undef LOG //#define ENABLE_LOG #ifdef ENABLE_LOG #define LOG(x) qDebug() << x #else #define LOG(x) ; #endif const int ViewMainPage::MaxViewCount = 6; /* * Layout of mThumbnailSplitter is: * * +-mThumbnailSplitter------------------------------------------------+ * |+-mAdapterContainer-----------------------------------------------+| * ||+-mDocumentViewContainer----------------------------------------+|| * |||+-DocumentView----------------++-DocumentView-----------------+||| * |||| || |||| * |||| || |||| * |||| || |||| * |||| || |||| * |||| || |||| * |||| || |||| * |||+-----------------------------++------------------------------+||| * ||+---------------------------------------------------------------+|| * ||+-mToolContainer------------------------------------------------+|| * ||| ||| * ||+---------------------------------------------------------------+|| * ||+-mStatusBarContainer-------------------------------------------+|| * |||[mToggleSideBarButton][mToggleThumbnailBarButton] [mZoomWidget]||| * ||+---------------------------------------------------------------+|| * |+-----------------------------------------------------------------+| * |===================================================================| * |+-mThumbnailBar---------------------------------------------------+| * || || * || || * |+-----------------------------------------------------------------+| * +-------------------------------------------------------------------+ */ struct ViewMainPagePrivate { ViewMainPage* q; SlideShow* mSlideShow; KActionCollection* mActionCollection; GvCore* mGvCore; KModelIndexProxyMapper* mDirModelToBarModelProxyMapper; QSplitter *mThumbnailSplitter; QWidget* mAdapterContainer; DocumentViewController* mDocumentViewController; QList mDocumentViews; DocumentViewSynchronizer* mSynchronizer; QToolButton* mToggleSideBarButton; QToolButton* mToggleThumbnailBarButton; ZoomWidget* mZoomWidget; DocumentViewContainer* mDocumentViewContainer; SlideContainer* mToolContainer; QWidget* mStatusBarContainer; ThumbnailBarView* mThumbnailBar; KToggleAction* mToggleThumbnailBarAction; KToggleAction* mSynchronizeAction; QCheckBox* mSynchronizeCheckBox; KSqueezedTextLabel* mDocumentCountLabel; // Activity Resource events reporting needs to be above KPart, // in the shell itself, to avoid problems with other MDI applications // that use this KPart QHash mActivityResources; bool mCompareMode; ZoomMode::Enum mZoomMode; void setupThumbnailBar() { mThumbnailBar = new ThumbnailBarView; ThumbnailBarItemDelegate* delegate = new ThumbnailBarItemDelegate(mThumbnailBar); mThumbnailBar->setItemDelegate(delegate); mThumbnailBar->setVisible(GwenviewConfig::thumbnailBarIsVisible()); mThumbnailBar->setSelectionMode(QAbstractItemView::ExtendedSelection); } void setupThumbnailBarStyleSheet() { QPalette pal = mGvCore->palette(GvCore::NormalViewPalette); mThumbnailBar->setPalette(pal); Qt::Orientation orientation = mThumbnailBar->orientation(); QColor bgColor = pal.color(QPalette::Normal, QPalette::Base); QColor bgSelColor = pal.color(QPalette::Normal, QPalette::Highlight); QColor bgHovColor = pal.color(QPalette::Normal, QPalette::Highlight); // Avoid dark and bright colors bgColor.setHsv(bgColor.hue(), bgColor.saturation(), (127 + 3 * bgColor.value()) / 4); // Hover uses lighter/faded version of select color. Combine with bgColor to adapt to different backgrounds bgHovColor.setHsv(bgHovColor.hue(), (bgHovColor.saturation() / 2), ((bgHovColor.value() + bgColor.value()) / 2)); QColor leftBorderColor = PaintUtils::adjustedHsv(bgColor, 0, 0, qMin(20, 255 - bgColor.value())); QColor rightBorderColor = PaintUtils::adjustedHsv(bgColor, 0, 0, -qMin(40, bgColor.value())); QColor borderSelColor = PaintUtils::adjustedHsv(bgSelColor, 0, 0, -qMin(60, bgSelColor.value())); QString itemCss = "QListView::item {" " background-color: %1;" " border-left: 1px solid %2;" " border-right: 1px solid %3;" "}"; itemCss = itemCss.arg( StyleSheetUtils::gradient(orientation, bgColor, 46), StyleSheetUtils::gradient(orientation, leftBorderColor, 36), StyleSheetUtils::gradient(orientation, rightBorderColor, 26)); QString itemSelCss = "QListView::item:selected {" " background-color: %1;" " border-left: 1px solid %2;" " border-right: 1px solid %2;" "}"; itemSelCss = itemSelCss.arg( StyleSheetUtils::gradient(orientation, bgSelColor, 56), StyleSheetUtils::rgba(borderSelColor)); QString itemHovCss = "QListView::item:hover:!selected {" " background-color: %1;" " border-left: 1px solid %2;" " border-right: 1px solid %3;" "}"; itemHovCss = itemHovCss.arg( StyleSheetUtils::gradient(orientation, bgHovColor, 56), StyleSheetUtils::rgba(leftBorderColor), StyleSheetUtils::rgba(rightBorderColor)); QString css = itemCss + itemSelCss + itemHovCss; if (orientation == Qt::Vertical) { css.replace("left", "top").replace("right", "bottom"); } mThumbnailBar->setStyleSheet(css); } void setupAdapterContainer() { mAdapterContainer = new QWidget; QVBoxLayout* layout = new QVBoxLayout(mAdapterContainer); layout->setMargin(0); layout->setSpacing(0); mDocumentViewContainer = new DocumentViewContainer; mDocumentViewContainer->setAutoFillBackground(true); mDocumentViewContainer->setBackgroundRole(QPalette::Base); layout->addWidget(mDocumentViewContainer); layout->addWidget(mToolContainer); layout->addWidget(mStatusBarContainer); } void setupDocumentViewController() { mDocumentViewController = new DocumentViewController(mActionCollection, q); mDocumentViewController->setZoomWidget(mZoomWidget); mDocumentViewController->setToolContainer(mToolContainer); mSynchronizer = new DocumentViewSynchronizer(&mDocumentViews, q); } DocumentView* createDocumentView() { DocumentView* view = mDocumentViewContainer->createView(); // Connect context menu // If you need to connect another view signal, make sure it is disconnected in deleteDocumentView QObject::connect(view, &DocumentView::contextMenuRequested, q, &ViewMainPage::showContextMenu); QObject::connect(view, &DocumentView::completed, q, &ViewMainPage::completed); QObject::connect(view, &DocumentView::previousImageRequested, q, &ViewMainPage::previousImageRequested); QObject::connect(view, &DocumentView::nextImageRequested, q, &ViewMainPage::nextImageRequested); QObject::connect(view, &DocumentView::openUrlRequested, q, &ViewMainPage::openUrlRequested); QObject::connect(view, &DocumentView::openDirUrlRequested, q, &ViewMainPage::openDirUrlRequested); QObject::connect(view, &DocumentView::captionUpdateRequested, q, &ViewMainPage::captionUpdateRequested); QObject::connect(view, &DocumentView::toggleFullScreenRequested, q, &ViewMainPage::toggleFullScreenRequested); QObject::connect(view, &DocumentView::focused, q, &ViewMainPage::slotViewFocused); QObject::connect(view, &DocumentView::hudTrashClicked, q, &ViewMainPage::trashView); QObject::connect(view, &DocumentView::hudDeselectClicked, q, &ViewMainPage::deselectView); QObject::connect(view, &DocumentView::videoFinished, mSlideShow, &SlideShow::resumeAndGoToNextUrl); mDocumentViews << view; mActivityResources.insert(view, new KActivities::ResourceInstance(q->window()->winId(), view)); return view; } void deleteDocumentView(DocumentView* view) { if (mDocumentViewController->view() == view) { mDocumentViewController->setView(nullptr); } // Make sure we do not get notified about this view while it is going away. // mDocumentViewController->deleteView() animates the view deletion so // the view still exists for a short while when we come back to the // event loop) QObject::disconnect(view, nullptr, q, nullptr); QObject::disconnect(view, nullptr, mSlideShow, nullptr); mDocumentViews.removeOne(view); mActivityResources.remove(view); mDocumentViewContainer->deleteView(view); } void setupToolContainer() { mToolContainer = new SlideContainer; } void setupStatusBar() { mStatusBarContainer = new QWidget; mToggleSideBarButton = new StatusBarToolButton; mToggleThumbnailBarButton = new StatusBarToolButton; mZoomWidget = new ZoomWidget; mSynchronizeCheckBox = new QCheckBox(i18n("Synchronize")); mSynchronizeCheckBox->hide(); mDocumentCountLabel = new KSqueezedTextLabel; mDocumentCountLabel->setAlignment(Qt::AlignCenter); mDocumentCountLabel->setTextElideMode(Qt::ElideRight); QMargins labelMargins = mDocumentCountLabel->contentsMargins(); labelMargins.setLeft(15); labelMargins.setRight(15); mDocumentCountLabel->setContentsMargins(labelMargins); QHBoxLayout* layout = new QHBoxLayout(mStatusBarContainer); layout->setMargin(0); layout->setSpacing(0); layout->addWidget(mToggleSideBarButton); layout->addWidget(mToggleThumbnailBarButton); layout->addStretch(); layout->addWidget(mSynchronizeCheckBox); // Ensure document count label takes up all available space, // so its autohide feature works properly (stretch factor = 1) layout->addWidget(mDocumentCountLabel, 1); layout->addStretch(); layout->addWidget(mZoomWidget); } void setupSplitter() { Qt::Orientation orientation = GwenviewConfig::thumbnailBarOrientation(); mThumbnailSplitter = new Splitter(orientation == Qt::Horizontal ? Qt::Vertical : Qt::Horizontal, q); mThumbnailBar->setOrientation(orientation); mThumbnailBar->setThumbnailAspectRatio(GwenviewConfig::thumbnailAspectRatio()); mThumbnailBar->setRowCount(GwenviewConfig::thumbnailBarRowCount()); mThumbnailSplitter->addWidget(mAdapterContainer); mThumbnailSplitter->addWidget(mThumbnailBar); mThumbnailSplitter->setSizes(GwenviewConfig::thumbnailSplitterSizes()); QVBoxLayout* layout = new QVBoxLayout(q); layout->setMargin(0); layout->addWidget(mThumbnailSplitter); } void saveSplitterConfig() { if (mThumbnailBar->isVisible()) { GwenviewConfig::setThumbnailSplitterSizes(mThumbnailSplitter->sizes()); } } DocumentView* currentView() const { return mDocumentViewController->view(); } void setCurrentView(DocumentView* view) { DocumentView* oldView = currentView(); if (view == oldView) { return; } if (oldView) { oldView->setCurrent(false); Q_ASSERT(mActivityResources.contains(oldView)); mActivityResources.value(oldView)->notifyFocusedOut(); } view->setCurrent(true); mDocumentViewController->setView(view); mSynchronizer->setCurrentView(view); QModelIndex index = indexForView(view); if (index.isValid()) { // Index may be invalid when Gwenview is started as // `gwenview /foo/image.png` because in this situation it loads image.png // *before* listing /foo (because it matters less to the user) mThumbnailBar->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Current); } Q_ASSERT(mActivityResources.contains(view)); mActivityResources.value(view)->notifyFocusedIn(); QObject::connect(view, &DocumentView::currentToolChanged, q, &ViewMainPage::updateFocus); } QModelIndex indexForView(DocumentView* view) const { QUrl url = view->url(); if (!url.isValid()) { qWarning() << "View does not display any document!"; return QModelIndex(); } SortedDirModel* dirModel = mGvCore->sortedDirModel(); QModelIndex srcIndex = dirModel->indexForUrl(url); if (!mDirModelToBarModelProxyMapper) { // Delay the initialization of the mapper to its first use because // mThumbnailBar->model() is not set after ViewMainPage ctor is // done. const_cast(this)->mDirModelToBarModelProxyMapper = new KModelIndexProxyMapper(dirModel, mThumbnailBar->model(), q); } QModelIndex index = mDirModelToBarModelProxyMapper->mapLeftToRight(srcIndex); return index; } void applyPalette(bool fullScreenMode) { mDocumentViewContainer->applyPalette(mGvCore->palette(fullScreenMode ? GvCore::FullScreenViewPalette : GvCore::NormalViewPalette)); setupThumbnailBarStyleSheet(); } void updateDocumentCountLabel() { const int current = mThumbnailBar->currentIndex().row() + 1; // zero-based const int total = mThumbnailBar->model()->rowCount(); const QString text = i18nc("@info:status %1 current document index, %2 total documents", "%1 of %2", current, total); mDocumentCountLabel->setText(text); } }; ViewMainPage::ViewMainPage(QWidget* parent, SlideShow* slideShow, KActionCollection* actionCollection, GvCore* gvCore) : QWidget(parent) , d(new ViewMainPagePrivate) { d->q = this; d->mDirModelToBarModelProxyMapper = nullptr; // Initialized later d->mSlideShow = slideShow; d->mActionCollection = actionCollection; d->mGvCore = gvCore; d->mCompareMode = false; QShortcut* enterKeyShortcut = new QShortcut(Qt::Key_Return, this); connect(enterKeyShortcut, &QShortcut::activated, this, &ViewMainPage::slotEnterPressed); d->setupToolContainer(); d->setupStatusBar(); d->setupAdapterContainer(); d->setupThumbnailBar(); d->setupSplitter(); d->setupDocumentViewController(); KActionCategory* view = new KActionCategory(i18nc("@title actions category - means actions changing smth in interface", "View"), actionCollection); - d->mToggleThumbnailBarAction = view->add(QString("toggle_thumbnailbar")); + d->mToggleThumbnailBarAction = view->add(QStringLiteral("toggle_thumbnailbar")); d->mToggleThumbnailBarAction->setText(i18n("Thumbnail Bar")); d->mToggleThumbnailBarAction->setIcon(QIcon::fromTheme("folder-image")); actionCollection->setDefaultShortcut(d->mToggleThumbnailBarAction, Qt::CTRL + Qt::Key_B); connect(d->mToggleThumbnailBarAction, &KToggleAction::triggered, this, &ViewMainPage::setThumbnailBarVisibility); d->mToggleThumbnailBarButton->setDefaultAction(d->mToggleThumbnailBarAction); d->mSynchronizeAction = view->add("synchronize_views"); d->mSynchronizeAction->setText(i18n("Synchronize")); actionCollection->setDefaultShortcut(d->mSynchronizeAction, Qt::CTRL + Qt::Key_Y); connect(d->mSynchronizeAction, SIGNAL(toggled(bool)), d->mSynchronizer, SLOT(setActive(bool))); // Ensure mSynchronizeAction and mSynchronizeCheckBox are in sync connect(d->mSynchronizeAction, SIGNAL(toggled(bool)), d->mSynchronizeCheckBox, SLOT(setChecked(bool))); connect(d->mSynchronizeCheckBox, SIGNAL(toggled(bool)), d->mSynchronizeAction, SLOT(setChecked(bool))); // Connections for the document count connect(d->mThumbnailBar, &ThumbnailBarView::rowsInsertedSignal, this, &ViewMainPage::slotDirModelItemsAddedOrRemoved); connect(d->mThumbnailBar, &ThumbnailBarView::rowsRemovedSignal, this, &ViewMainPage::slotDirModelItemsAddedOrRemoved); installEventFilter(this); } ViewMainPage::~ViewMainPage() { delete d; } void ViewMainPage::loadConfig() { d->applyPalette(window()->isFullScreen()); // FIXME: Not symetric with saveConfig(). Check if it matters. Q_FOREACH(DocumentView * view, d->mDocumentViews) { view->loadAdapterConfig(); } Qt::Orientation orientation = GwenviewConfig::thumbnailBarOrientation(); d->mThumbnailSplitter->setOrientation(orientation == Qt::Horizontal ? Qt::Vertical : Qt::Horizontal); d->mThumbnailBar->setOrientation(orientation); d->setupThumbnailBarStyleSheet(); d->mThumbnailBar->setVisible(GwenviewConfig::thumbnailBarIsVisible()); d->mToggleThumbnailBarAction->setChecked(GwenviewConfig::thumbnailBarIsVisible()); int oldRowCount = d->mThumbnailBar->rowCount(); int newRowCount = GwenviewConfig::thumbnailBarRowCount(); if (oldRowCount != newRowCount) { d->mThumbnailBar->setUpdatesEnabled(false); int gridSize = d->mThumbnailBar->gridSize().width(); d->mThumbnailBar->setRowCount(newRowCount); // Adjust splitter to ensure thumbnail size remains the same int delta = (newRowCount - oldRowCount) * gridSize; QList sizes = d->mThumbnailSplitter->sizes(); Q_ASSERT(sizes.count() == 2); sizes[0] -= delta; sizes[1] += delta; d->mThumbnailSplitter->setSizes(sizes); d->mThumbnailBar->setUpdatesEnabled(true); } d->mZoomMode = GwenviewConfig::zoomMode(); } void ViewMainPage::saveConfig() { d->saveSplitterConfig(); GwenviewConfig::setThumbnailBarIsVisible(d->mToggleThumbnailBarAction->isChecked()); } void ViewMainPage::setThumbnailBarVisibility(bool visible) { d->saveSplitterConfig(); d->mThumbnailBar->setVisible(visible); } int ViewMainPage::statusBarHeight() const { return d->mStatusBarContainer->height(); } void ViewMainPage::setStatusBarVisible(bool visible) { d->mStatusBarContainer->setVisible(visible); } void ViewMainPage::setFullScreenMode(bool fullScreenMode) { if (fullScreenMode) { d->mThumbnailBar->setVisible(false); } else { d->mThumbnailBar->setVisible(d->mToggleThumbnailBarAction->isChecked()); } d->applyPalette(fullScreenMode); d->mToggleThumbnailBarAction->setEnabled(!fullScreenMode); } ThumbnailBarView* ViewMainPage::thumbnailBar() const { return d->mThumbnailBar; } inline void addActionToMenu(QMenu* menu, KActionCollection* actionCollection, const char* name) { QAction* action = actionCollection->action(name); if (action) { menu->addAction(action); } } void ViewMainPage::showContextMenu() { QMenu menu(this); addActionToMenu(&menu, d->mActionCollection, "fullscreen"); menu.addSeparator(); addActionToMenu(&menu, d->mActionCollection, "go_previous"); addActionToMenu(&menu, d->mActionCollection, "go_next"); if (d->currentView()->canZoom()) { menu.addSeparator(); addActionToMenu(&menu, d->mActionCollection, "view_actual_size"); addActionToMenu(&menu, d->mActionCollection, "view_zoom_to_fit"); addActionToMenu(&menu, d->mActionCollection, "view_zoom_in"); addActionToMenu(&menu, d->mActionCollection, "view_zoom_out"); } if (d->mCompareMode) { menu.addSeparator(); addActionToMenu(&menu, d->mActionCollection, "synchronize_views"); } menu.addSeparator(); addActionToMenu(&menu, d->mActionCollection, "file_copy_to"); addActionToMenu(&menu, d->mActionCollection, "file_move_to"); addActionToMenu(&menu, d->mActionCollection, "file_link_to"); menu.addSeparator(); addActionToMenu(&menu, d->mActionCollection, "file_open_with"); addActionToMenu(&menu, d->mActionCollection, "file_open_containing_folder"); menu.exec(QCursor::pos()); } QSize ViewMainPage::sizeHint() const { return QSize(400, 300); } QSize ViewMainPage::minimumSizeHint() const { if (!layout()) { return QSize(); } QSize minimumSize = layout()->minimumSize(); if (window()->isFullScreen()) { // Check minimum width of the overlay fullscreen bar // since there is no layout link which could do this const FullScreenBar* fullScreenBar = findChild(); if (fullScreenBar && fullScreenBar->layout()) { const int fullScreenBarWidth = fullScreenBar->layout()->minimumSize().width(); if (fullScreenBarWidth > minimumSize.width()) { minimumSize.setWidth(fullScreenBarWidth); } } } return minimumSize; } QUrl ViewMainPage::url() const { GV_RETURN_VALUE_IF_FAIL(d->currentView(), QUrl()); return d->currentView()->url(); } Document::Ptr ViewMainPage::currentDocument() const { if (!d->currentView()) { LOG("!d->documentView()"); return Document::Ptr(); } return d->currentView()->document(); } bool ViewMainPage::isEmpty() const { return !currentDocument(); } RasterImageView* ViewMainPage::imageView() const { if (!d->currentView()) { return nullptr; } return d->currentView()->imageView(); } DocumentView* ViewMainPage::documentView() const { return d->currentView(); } void ViewMainPage::openUrl(const QUrl &url) { openUrls(QList() << url, url); } void ViewMainPage::openUrls(const QList& allUrls, const QUrl ¤tUrl) { DocumentView::Setup setup; QSet urls = allUrls.toSet(); d->mCompareMode = urls.count() > 1; typedef QMap ViewForUrlMap; ViewForUrlMap viewForUrlMap; if (!d->mDocumentViews.isEmpty()) { d->mDocumentViewContainer->updateSetup(d->mDocumentViews.last()); } if (d->mDocumentViews.isEmpty() || d->mZoomMode == ZoomMode::Autofit) { setup.valid = true; setup.zoomToFit = true; } else { setup = d->mDocumentViews.last()->setup(); } // Destroy views which show urls we don't care about, remove from "urls" the // urls which already have a view. Q_FOREACH(DocumentView * view, d->mDocumentViews) { QUrl url = view->url(); if (urls.contains(url)) { // view displays an url we must display, keep it urls.remove(url); viewForUrlMap.insert(url, view); } else { // view url is not interesting, drop it d->deleteDocumentView(view); } } // Create view for remaining urls Q_FOREACH(const QUrl &url, urls) { if (d->mDocumentViews.count() >= MaxViewCount) { qWarning() << "Too many documents to show"; break; } DocumentView* view = d->createDocumentView(); viewForUrlMap.insert(url, view); } // Set sortKey to match url order int sortKey = 0; Q_FOREACH(const QUrl &url, allUrls) { viewForUrlMap[url]->setSortKey(sortKey); ++sortKey; } d->mDocumentViewContainer->updateLayout(); // Load urls for new views. Do it only now because the view must have the // correct size before it starts loading its url. Do not do it later because // view->url() needs to be set for the next loop. ViewForUrlMap::ConstIterator it = viewForUrlMap.constBegin(), end = viewForUrlMap.constEnd(); for (; it != end; ++it) { QUrl url = it.key(); DocumentView* view = it.value(); DocumentView::Setup savedSetup = d->mDocumentViewContainer->savedSetup(url); view->openUrl(url, d->mZoomMode == ZoomMode::Individual && savedSetup.valid ? savedSetup : setup); d->mActivityResources.value(view)->setUri(url); } // Init views Q_FOREACH(DocumentView * view, d->mDocumentViews) { view->setCompareMode(d->mCompareMode); if (view->url() == currentUrl) { d->setCurrentView(view); } else { view->setCurrent(false); } } d->mSynchronizeCheckBox->setVisible(d->mCompareMode); if (d->mCompareMode) { d->mSynchronizer->setActive(d->mSynchronizeCheckBox->isChecked()); } else { d->mSynchronizer->setActive(false); } d->updateDocumentCountLabel(); d->mDocumentCountLabel->setVisible(!d->mCompareMode); } void ViewMainPage::reload() { DocumentView *view = d->currentView(); if (!view) { return; } Document::Ptr doc = view->document(); if (!doc) { qWarning() << "!doc"; return; } if (doc->isModified()) { KGuiItem cont = KStandardGuiItem::cont(); cont.setText(i18nc("@action:button", "Discard Changes and Reload")); int answer = KMessageBox::warningContinueCancel(this, i18nc("@info", "This image has been modified. Reloading it will discard all your changes."), QString() /* caption */, cont); if (answer != KMessageBox::Continue) { return; } } doc->reload(); // Call openUrl again because DocumentView may need to switch to a new // adapter (for example because document was broken and it is not anymore) d->currentView()->openUrl(doc->url(), d->currentView()->setup()); } void ViewMainPage::reset() { d->mDocumentViewController->reset(); d->mDocumentViewContainer->reset(); d->mDocumentViews.clear(); } void ViewMainPage::slotViewFocused(DocumentView* view) { d->setCurrentView(view); } void ViewMainPage::slotEnterPressed() { DocumentView *view = d->currentView(); if (view) { AbstractRasterImageViewTool *tool = view->currentTool(); if (tool) { QKeyEvent event(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier); tool->keyPressEvent(&event); if (event.isAccepted()) { return; } } } emit goToBrowseModeRequested(); } bool ViewMainPage::eventFilter(QObject* watched, QEvent* event) { if (event->type() == QEvent::ShortcutOverride) { const QKeyEvent* keyEvent = static_cast(event); if (keyEvent->key() == Qt::Key_Escape) { const DocumentView* view = d->currentView(); if (view) { AbstractRasterImageViewTool* tool = view->currentTool(); if (tool) { QKeyEvent toolKeyEvent(QEvent::KeyPress, Qt::Key_Escape, Qt::NoModifier); tool->keyPressEvent(&toolKeyEvent); if (toolKeyEvent.isAccepted()) { event->accept(); } } } } } return QWidget::eventFilter(watched, event); } void ViewMainPage::trashView(DocumentView* view) { QUrl url = view->url(); deselectView(view); FileOperations::trash(QList() << url, this); } void ViewMainPage::deselectView(DocumentView* view) { DocumentView* newCurrentView = nullptr; if (view == d->currentView()) { // We need to find a new view to set as current int idx = d->mDocumentViews.indexOf(view); if (idx + 1 < d->mDocumentViews.count()) { newCurrentView = d->mDocumentViews.at(idx + 1); } else if (idx > 0) { newCurrentView = d->mDocumentViews.at(idx - 1); } else { GV_WARN_AND_RETURN("No view found to set as current"); } } QModelIndex index = d->indexForView(view); QItemSelectionModel* selectionModel = d->mThumbnailBar->selectionModel(); selectionModel->select(index, QItemSelectionModel::Deselect); if (newCurrentView) { d->setCurrentView(newCurrentView); } } QToolButton* ViewMainPage::toggleSideBarButton() const { return d->mToggleSideBarButton; } void ViewMainPage::showMessageWidget(QGraphicsWidget* widget, Qt::Alignment align) { d->mDocumentViewContainer->showMessageWidget(widget, align); } void ViewMainPage::updateFocus(const AbstractRasterImageViewTool* tool) { if (!tool) { d->mDocumentViewContainer->setFocus(); } } void ViewMainPage::slotDirModelItemsAddedOrRemoved() { d->updateDocumentCountLabel(); } } // namespace diff --git a/app/viewmainpage.h b/app/viewmainpage.h index 1de429d8..8dfaec47 100644 --- a/app/viewmainpage.h +++ b/app/viewmainpage.h @@ -1,166 +1,166 @@ /* 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. */ #ifndef VIEWMAINPAGE_H #define VIEWMAINPAGE_H // Local #include // KDE #include // Qt #include #include class QGraphicsWidget; class KActionCollection; namespace Gwenview { class AbstractRasterImageViewTool; class DocumentView; class GvCore; class RasterImageView; class SlideShow; class ThumbnailBarView; struct ViewMainPagePrivate; /** * Holds the active document view and associated widgetry. */ class ViewMainPage : public QWidget { Q_OBJECT public: static const int MaxViewCount; ViewMainPage(QWidget* parent, SlideShow*, KActionCollection*, GvCore*); - ~ViewMainPage() Q_DECL_OVERRIDE; + ~ViewMainPage() override; ThumbnailBarView* thumbnailBar() const; void loadConfig(); void saveConfig(); /** * Reset the view */ void reset(); void setFullScreenMode(bool fullScreen); int statusBarHeight() const; - QSize sizeHint() const Q_DECL_OVERRIDE; - QSize minimumSizeHint() const Q_DECL_OVERRIDE; + QSize sizeHint() const override; + QSize minimumSizeHint() const override; /** * Returns the url of the current document, or an invalid url if unknown */ QUrl url() const; void openUrl(const QUrl &url); /** * Opens up to MaxViewCount urls, and set currentUrl as the current one */ void openUrls(const QList& urls, const QUrl ¤tUrl); void reload(); Document::Ptr currentDocument() const; bool isEmpty() const; /** * Returns the image view, if the current adapter has one. */ RasterImageView* imageView() const; /** * Returns the document view */ DocumentView* documentView() const; /** * Sets a widget to show at the bottom of the panel */ void setToolWidget(QWidget* widget); QToolButton* toggleSideBarButton() const; void showMessageWidget(QGraphicsWidget*, Qt::Alignment align = Qt::AlignHCenter | Qt::AlignTop); Q_SIGNALS: /** * Emitted when the part has finished loading */ void completed(); void previousImageRequested(); void nextImageRequested(); void openUrlRequested(const QUrl&); void openDirUrlRequested(const QUrl&); void toggleFullScreenRequested(); void goToBrowseModeRequested(); void captionUpdateRequested(const QString&); public Q_SLOTS: void setStatusBarVisible(bool); private Q_SLOTS: void setThumbnailBarVisibility(bool visible); void showContextMenu(); void slotViewFocused(DocumentView*); void slotEnterPressed(); void trashView(DocumentView*); void deselectView(DocumentView*); void slotDirModelItemsAddedOrRemoved(); protected: - bool eventFilter(QObject* watched, QEvent* event) Q_DECL_OVERRIDE; + bool eventFilter(QObject* watched, QEvent* event) override; private: friend struct ViewMainPagePrivate; ViewMainPagePrivate* const d; void updateFocus(const AbstractRasterImageViewTool* tool); }; } // namespace #endif /* VIEWMAINPAGE_H */ diff --git a/importer/importdialog.h b/importer/importdialog.h index 6a79bc85..3e435c61 100644 --- a/importer/importdialog.h +++ b/importer/importdialog.h @@ -1,59 +1,59 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2009 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, Cambridge, MA 02110-1301, USA. */ #ifndef IMPORTDIALOG_H #define IMPORTDIALOG_H // Qt #include // KDE #include // Local namespace Gwenview { class ImportDialogPrivate; class ImportDialog : public KMainWindow { Q_OBJECT public: ImportDialog(); - ~ImportDialog() Q_DECL_OVERRIDE; + ~ImportDialog() override; - QSize sizeHint() const Q_DECL_OVERRIDE; + QSize sizeHint() const override; public Q_SLOTS: void setSourceUrl(const QUrl&, const QString& deviceUdi); private Q_SLOTS: void startImport(); void slotImportFinished(); void showImportError(const QString&); private: ImportDialogPrivate* const d; }; } // namespace #endif /* IMPORTDIALOG_H */ diff --git a/importer/importerconfigdialog.cpp b/importer/importerconfigdialog.cpp index 00fe786c..363a602d 100644 --- a/importer/importerconfigdialog.cpp +++ b/importer/importerconfigdialog.cpp @@ -1,99 +1,99 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2009 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, Cambridge, MA 02110-1301, USA. */ // Self #include "importerconfigdialog.h" // Qt #include // KDE #include // Local #include "filenameformater.h" #include "importerconfig.h" #include "ui_importerconfigdialog.h" namespace Gwenview { static const QString PREVIEW_FILENAME = "PICT0012.JPG"; static const QDateTime PREVIEW_DATETIME = QDateTime(QDate(2009, 10, 25), QTime(17, 51, 18)); struct ImporterConfigDialogPrivate : public Ui_ImporterConfigDialog { ImporterConfigDialog* q; void setupHelpText() { QString helpText = "
    "; FileNameFormater::HelpMap map = FileNameFormater::helpMap(); FileNameFormater::HelpMap::ConstIterator it = map.constBegin(), end = map.constEnd(); for (; it != end; ++it) { QString keyword = '{' + it.key() + '}'; QString explanation = it.value().toHtmlEscaped(); - QString link = QString("%1").arg(keyword); + QString link = QStringLiteral("%1").arg(keyword); helpText += "
  • " + i18nc("%1 is the importer keyword, %2 is keyword explanation", "%1: %2", link, explanation) + "
  • "; } helpText += "
"; mRenameFormatHelpLabel->setText(helpText); QObject::connect(mRenameFormatHelpLabel, SIGNAL(linkActivated(QString)), q, SLOT(slotHelpLinkActivated(QString))); } }; ImporterConfigDialog::ImporterConfigDialog(QWidget* parent) : KConfigDialog(parent, "Importer Settings", ImporterConfig::self()) , d(new ImporterConfigDialogPrivate) { d->q = this; QWidget* widget = new QWidget; d->setupUi(widget); setFaceType(KPageDialog::Plain); addPage(widget, QString()); connect(d->kcfg_AutoRenameFormat, SIGNAL(textChanged(QString)), SLOT(updatePreview())); d->setupHelpText(); updatePreview(); } ImporterConfigDialog::~ImporterConfigDialog() { delete d; } void ImporterConfigDialog::slotHelpLinkActivated(const QString& keyword) { d->kcfg_AutoRenameFormat->insert(keyword); } void ImporterConfigDialog::updatePreview() { FileNameFormater formater(d->kcfg_AutoRenameFormat->text()); d->mPreviewOutputLabel->setText(formater.format(QUrl::fromLocalFile('/' + PREVIEW_FILENAME), PREVIEW_DATETIME)); } } // namespace diff --git a/importer/thumbnailpage.cpp b/importer/thumbnailpage.cpp index c4634185..d39c9a09 100644 --- a/importer/thumbnailpage.cpp +++ b/importer/thumbnailpage.cpp @@ -1,470 +1,470 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2009 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, Cambridge, MA 02110-1301, USA. */ // Self #include "thumbnailpage.h" #include "dialogguard.h" // Qt #include #include #include #include #include // KDE #include #include #include #include #include #include #include // Local #include #include #include #include #include #include #include #include #include #include #include #include namespace Gwenview { static const int DEFAULT_THUMBNAIL_SIZE = 128; static const qreal DEFAULT_THUMBNAIL_ASPECT_RATIO = 3. / 2.; static const char* URL_FOR_BASE_URL_GROUP = "UrlForBaseUrl"; class ImporterThumbnailViewHelper : public AbstractThumbnailViewHelper { public: ImporterThumbnailViewHelper(QObject* parent) : AbstractThumbnailViewHelper(parent) {} - void showContextMenu(QWidget*) Q_DECL_OVERRIDE + void showContextMenu(QWidget*) override {} - void showMenuForUrlDroppedOnViewport(QWidget*, const QList&) Q_DECL_OVERRIDE + void showMenuForUrlDroppedOnViewport(QWidget*, const QList&) override {} - void showMenuForUrlDroppedOnDir(QWidget*, const QList&, const QUrl&) Q_DECL_OVERRIDE + void showMenuForUrlDroppedOnDir(QWidget*, const QList&, const QUrl&) override {} }; inline KFileItem itemForIndex(const QModelIndex& index) { return index.data(KDirModel::FileItemRole).value(); } struct ThumbnailPagePrivate : public Ui_ThumbnailPage { ThumbnailPage* q; SerializedUrlMap mUrlMap; QIcon mSrcBaseIcon; QString mSrcBaseName; QUrl mSrcBaseUrl; QUrl mSrcUrl; KModelIndexProxyMapper* mSrcUrlModelProxyMapper; RecursiveDirModel* mRecursiveDirModel; QAbstractItemModel* mFinalModel; ThumbnailProvider mThumbnailProvider; QPushButton* mImportSelectedButton; QPushButton* mImportAllButton; QList mUrlList; void setupDirModel() { mRecursiveDirModel = new RecursiveDirModel(q); KindProxyModel* kindProxyModel = new KindProxyModel(q); kindProxyModel->setKindFilter( MimeTypeUtils::KIND_RASTER_IMAGE | MimeTypeUtils::KIND_SVG_IMAGE | MimeTypeUtils::KIND_VIDEO); kindProxyModel->setSourceModel(mRecursiveDirModel); QSortFilterProxyModel *sortModel = new QSortFilterProxyModel(q); sortModel->setDynamicSortFilter(true); sortModel->setSourceModel(kindProxyModel); sortModel->sort(0); mFinalModel = sortModel; QObject::connect( mFinalModel, SIGNAL(rowsInserted(QModelIndex,int,int)), q, SLOT(updateImportButtons())); QObject::connect( mFinalModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), q, SLOT(updateImportButtons())); QObject::connect( mFinalModel, SIGNAL(modelReset()), q, SLOT(updateImportButtons())); } void setupIcons() { const KIconLoader::Group group = KIconLoader::NoGroup; const int size = KIconLoader::SizeHuge; mSrcIconLabel->setPixmap(KIconLoader::global()->loadIcon("camera-photo", group, size)); mDstIconLabel->setPixmap(KIconLoader::global()->loadIcon("computer", group, size)); } void setupSrcUrlWidgets() { mSrcUrlModelProxyMapper = nullptr; QObject::connect(mSrcUrlButton, SIGNAL(clicked()), q, SLOT(setupSrcUrlTreeView())); QObject::connect(mSrcUrlButton, SIGNAL(clicked()), q, SLOT(toggleSrcUrlTreeView())); mSrcUrlTreeView->hide(); KAcceleratorManager::setNoAccel(mSrcUrlButton); } void setupDstUrlRequester() { mDstUrlRequester->setMode(KFile::Directory | KFile::LocalOnly); } void setupThumbnailView() { mThumbnailView->setModel(mFinalModel); mThumbnailView->setSelectionMode(QAbstractItemView::ExtendedSelection); mThumbnailView->setThumbnailViewHelper(new ImporterThumbnailViewHelper(q)); PreviewItemDelegate* delegate = new PreviewItemDelegate(mThumbnailView); delegate->setThumbnailDetails(PreviewItemDelegate::FileNameDetail); PreviewItemDelegate::ContextBarActions actions; switch (GwenviewConfig::thumbnailActions()) { case ThumbnailActions::None: actions = PreviewItemDelegate::NoAction; break; case ThumbnailActions::ShowSelectionButtonOnly: case ThumbnailActions::AllButtons: actions = PreviewItemDelegate::SelectionAction; break; } delegate->setContextBarActions(actions); mThumbnailView->setItemDelegate(delegate); // Colors int value = GwenviewConfig::viewBackgroundValue(); QColor bgColor = QColor::fromHsv(0, 0, value); QColor fgColor = value > 128 ? Qt::black : Qt::white; QPalette pal = mThumbnailView->palette(); pal.setColor(QPalette::Base, bgColor); pal.setColor(QPalette::Text, fgColor); mThumbnailView->setPalette(pal); QObject::connect(mSlider, SIGNAL(valueChanged(int)), mThumbnailView, SLOT(setThumbnailWidth(int))); QObject::connect(mThumbnailView, SIGNAL(thumbnailWidthChanged(int)), mSlider, SLOT(setValue(int))); int thumbnailSize = DEFAULT_THUMBNAIL_SIZE; mSlider->setValue(thumbnailSize); mSlider->updateToolTip(); mThumbnailView->setThumbnailAspectRatio(DEFAULT_THUMBNAIL_ASPECT_RATIO); mThumbnailView->setThumbnailWidth(thumbnailSize); mThumbnailView->setThumbnailProvider(&mThumbnailProvider); QObject::connect( mThumbnailView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), q, SLOT(updateImportButtons())); } void setupButtonBox() { QObject::connect(mConfigureButton, SIGNAL(clicked()), q, SLOT(showConfigDialog())); mImportSelectedButton = mButtonBox->addButton(i18n("Import Selected"), QDialogButtonBox::AcceptRole); QObject::connect(mImportSelectedButton, SIGNAL(clicked(bool)), q, SLOT(slotImportSelected())); mImportAllButton = mButtonBox->addButton(i18n("Import All"), QDialogButtonBox::AcceptRole); QObject::connect(mImportAllButton, SIGNAL(clicked(bool)), q, SLOT(slotImportAll())); QObject::connect( mButtonBox, SIGNAL(rejected()), q, SIGNAL(rejected())); } QUrl urlForBaseUrl() const { QUrl url = mUrlMap.value(mSrcBaseUrl); if (!url.isValid()) { return QUrl(); } KIO::StatJob *job = KIO::stat(url); KJobWidgets::setWindow(job, q); if (!job->exec()) { return QUrl(); } KFileItem item(job->statResult(), url, true /* delayedMimeTypes */); return item.isDir() ? url : QUrl(); } void rememberUrl(const QUrl& url) { mUrlMap.insert(mSrcBaseUrl, url); } }; ThumbnailPage::ThumbnailPage() : d(new ThumbnailPagePrivate) { d->q = this; d->mUrlMap.setConfigGroup(KConfigGroup(KSharedConfig::openConfig(), URL_FOR_BASE_URL_GROUP)); d->setupUi(this); d->setupIcons(); d->setupDirModel(); d->setupSrcUrlWidgets(); d->setupDstUrlRequester(); d->setupThumbnailView(); d->setupButtonBox(); updateImportButtons(); } ThumbnailPage::~ThumbnailPage() { delete d; } void ThumbnailPage::setSourceUrl(const QUrl& srcBaseUrl, const QString& iconName, const QString& name) { d->mSrcBaseIcon = QIcon::fromTheme(iconName); d->mSrcBaseName = name; const int size = KIconLoader::SizeHuge; d->mSrcIconLabel->setPixmap(d->mSrcBaseIcon.pixmap(size)); d->mSrcBaseUrl = srcBaseUrl; if (!d->mSrcBaseUrl.path().endsWith('/')) { d->mSrcBaseUrl.setPath(d->mSrcBaseUrl.path() + '/'); } QUrl url = d->urlForBaseUrl(); if (url.isValid()) { openUrl(url); } else { DocumentDirFinder* finder = new DocumentDirFinder(srcBaseUrl); connect(finder, SIGNAL(done(QUrl,DocumentDirFinder::Status)), SLOT(slotDocumentDirFinderDone(QUrl,DocumentDirFinder::Status))); finder->start(); } } void ThumbnailPage::slotDocumentDirFinderDone(const QUrl& url, DocumentDirFinder::Status /*status*/) { d->rememberUrl(url); openUrl(url); } void ThumbnailPage::openUrl(const QUrl& url) { d->mSrcUrl = url; QString path = QDir(d->mSrcBaseUrl.path()).relativeFilePath(d->mSrcUrl.path()); QString text; if (path.isEmpty() || path == ".") { text = d->mSrcBaseName; } else { path = QUrl::fromPercentEncoding(path.toUtf8()); path.replace("/", QString::fromUtf8(" › ")); text = QString::fromUtf8("%1 › %2").arg(d->mSrcBaseName).arg(path); } d->mSrcUrlButton->setText(text); d->mRecursiveDirModel->setUrl(url); } QList ThumbnailPage::urlList() const { return d->mUrlList; } void ThumbnailPage::setDestinationUrl(const QUrl& url) { d->mDstUrlRequester->setUrl(url); } QUrl ThumbnailPage::destinationUrl() const { return d->mDstUrlRequester->url(); } void ThumbnailPage::slotImportSelected() { importList(d->mThumbnailView->selectionModel()->selectedIndexes()); } void ThumbnailPage::slotImportAll() { QModelIndexList list; QAbstractItemModel* model = d->mThumbnailView->model(); for (int row = model->rowCount() - 1; row >= 0; --row) { list << model->index(row, 0); } importList(list); } void ThumbnailPage::importList(const QModelIndexList& list) { d->mUrlList.clear(); Q_FOREACH(const QModelIndex & index, list) { KFileItem item = itemForIndex(index); if (!ArchiveUtils::fileItemIsDirOrArchive(item)) { d->mUrlList << item.url(); } // FIXME: Handle dirs (do we want to import recursively?) } emit importRequested(); } void ThumbnailPage::updateImportButtons() { d->mImportSelectedButton->setEnabled(d->mThumbnailView->selectionModel()->hasSelection()); d->mImportAllButton->setEnabled(d->mThumbnailView->model()->rowCount(QModelIndex()) > 0); } void ThumbnailPage::showConfigDialog() { DialogGuard dialog(this); dialog->exec(); } /** * This model allows only the url passed in the constructor to appear at the root * level. This makes it possible to select the url, but not its siblings. * It also provides custom role values for the root item. */ class OnlyBaseUrlProxyModel : public QSortFilterProxyModel { public: OnlyBaseUrlProxyModel(const QUrl& url, const QIcon& icon, const QString& name, QObject* parent) : QSortFilterProxyModel(parent) , mUrl(url) , mIcon(icon) , mName(name) {} bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const // reimp - Q_DECL_OVERRIDE { + override { if (sourceParent.isValid()) { return true; } QModelIndex index = sourceModel()->index(sourceRow, 0); KFileItem item = itemForIndex(index); return item.url().matches(mUrl, QUrl::StripTrailingSlash); } QVariant data(const QModelIndex& index, int role) const // reimp - Q_DECL_OVERRIDE { + override { if (index.parent().isValid()) { return QSortFilterProxyModel::data(index, role); } switch (role) { case Qt::DisplayRole: return mName; case Qt::DecorationRole: return mIcon; case Qt::ToolTipRole: return mUrl.toDisplayString(QUrl::PreferLocalFile); default: return QSortFilterProxyModel::data(index, role); } } private: QUrl mUrl; QIcon mIcon; QString mName; }; void ThumbnailPage::setupSrcUrlTreeView() { if (d->mSrcUrlTreeView->model()) { // Already initialized return; } KDirModel* dirModel = new KDirModel(this); dirModel->dirLister()->setDirOnlyMode(true); dirModel->dirLister()->openUrl(KIO::upUrl(d->mSrcBaseUrl)); OnlyBaseUrlProxyModel* onlyBaseUrlModel = new OnlyBaseUrlProxyModel(d->mSrcBaseUrl, d->mSrcBaseIcon, d->mSrcBaseName, this); onlyBaseUrlModel->setSourceModel(dirModel); QSortFilterProxyModel* sortModel = new QSortFilterProxyModel(this); sortModel->setDynamicSortFilter(true); sortModel->setSourceModel(onlyBaseUrlModel); sortModel->sort(0); d->mSrcUrlModelProxyMapper = new KModelIndexProxyMapper(dirModel, sortModel, this); d->mSrcUrlTreeView->setModel(sortModel); for(int i = 1; i < dirModel->columnCount(); ++i) { d->mSrcUrlTreeView->hideColumn(i); } connect(d->mSrcUrlTreeView, SIGNAL(activated(QModelIndex)), SLOT(openUrlFromIndex(QModelIndex))); connect(d->mSrcUrlTreeView, SIGNAL(clicked(QModelIndex)), SLOT(openUrlFromIndex(QModelIndex))); dirModel->expandToUrl(d->mSrcUrl); connect(dirModel, SIGNAL(expand(QModelIndex)), SLOT(slotSrcUrlModelExpand(QModelIndex))); } void ThumbnailPage::slotSrcUrlModelExpand(const QModelIndex& index) { QModelIndex viewIndex = d->mSrcUrlModelProxyMapper->mapLeftToRight(index); d->mSrcUrlTreeView->expand(viewIndex); KFileItem item = itemForIndex(index); if (item.url() == d->mSrcUrl) { d->mSrcUrlTreeView->selectionModel()->select(viewIndex, QItemSelectionModel::ClearAndSelect); } } void ThumbnailPage::toggleSrcUrlTreeView() { d->mSrcUrlTreeView->setVisible(!d->mSrcUrlTreeView->isVisible()); } void ThumbnailPage::openUrlFromIndex(const QModelIndex& index) { KFileItem item = itemForIndex(index); if (item.isNull()) { return; } QUrl url = item.url(); d->rememberUrl(url); openUrl(url); } } // namespace diff --git a/lib/about.cpp b/lib/about.cpp index 82377c2d..31319166 100644 --- a/lib/about.cpp +++ b/lib/about.cpp @@ -1,56 +1,56 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2012 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, see . */ // Self #include "about.h" // Local #include "gwenview_version.h" // KDE #include #include // Qt #include namespace Gwenview { KAboutData* createAboutData(const QString& appName, const QString& programName) { KAboutData* data = new KAboutData(appName, programName, QStringLiteral(GWENVIEW_VERSION_STRING)); data->setLicense(KAboutLicense::GPL); - data->setCopyrightStatement(i18n("Copyright 2000-2014 Gwenview authors")); + data->setCopyrightStatement(i18n("Copyright 2000-2018 Gwenview authors")); data->setProductName("gwenview"); data->addAuthor( QStringLiteral("Lukáš Tinkl"), i18n("Current Maintainer"), - "ltinkl@redhat.com"); + QStringLiteral("ltinkl@redhat.com")); data->addAuthor( QStringLiteral("Aurélien Gâteau"), i18n("Developer"), - "agateau@kde.org"); + QStringLiteral("agateau@kde.org")); data->addAuthor( QStringLiteral("Benjamin Löwe"), i18n("Developer"), - "benni@mytum.de"); + QStringLiteral("benni@mytum.de")); return data; } } // namespace diff --git a/lib/abstractimageoperation.cpp b/lib/abstractimageoperation.cpp index 84d63ccc..5bc6381e 100644 --- a/lib/abstractimageoperation.cpp +++ b/lib/abstractimageoperation.cpp @@ -1,129 +1,129 @@ // 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 "abstractimageoperation.h" // Qt #include #include // KDE #include // Local #include "document/documentfactory.h" #include "document/documentjob.h" namespace Gwenview { class ImageOperationCommand : public QUndoCommand { public: ImageOperationCommand(AbstractImageOperation* op) : mOp(op) {} ~ImageOperationCommand() { delete mOp; } - void undo() Q_DECL_OVERRIDE + void undo() override { mOp->undo(); } void redo() override { mOp->redo(); } private: AbstractImageOperation* mOp; }; struct AbstractImageOperationPrivate { QString mText; QUrl mUrl; ImageOperationCommand* mCommand; }; AbstractImageOperation::AbstractImageOperation() : d(new AbstractImageOperationPrivate) { } AbstractImageOperation::~AbstractImageOperation() { delete d; } void AbstractImageOperation::applyToDocument(Document::Ptr doc) { d->mUrl = doc->url(); d->mCommand = new ImageOperationCommand(this); d->mCommand->setText(d->mText); // QUndoStack::push() executes command by calling its redo() function doc->undoStack()->push(d->mCommand); } Document::Ptr AbstractImageOperation::document() const { Document::Ptr doc = DocumentFactory::instance()->load(d->mUrl); doc->startLoadingFullImage(); return doc; } void AbstractImageOperation::finish(bool ok) { if (ok) { // Give QUndoStack time to update in case the redo/undo is executed immediately // (e.g. undo crop just sets the previous image) QTimer::singleShot(0, document().data(), &Document::imageOperationCompleted); } else { #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) // Remove command from undo stack without executing undo() d->mCommand->setObsolete(true); #endif document()->undoStack()->undo(); } } void AbstractImageOperation::finishFromKJob(KJob* job) { finish(job->error() == KJob::NoError); } void AbstractImageOperation::setText(const QString& text) { d->mText = text; } void AbstractImageOperation::redoAsDocumentJob(DocumentJob* job) { connect(job, SIGNAL(result(KJob*)), SLOT(finishFromKJob(KJob*))); document()->enqueueJob(job); } } // namespace diff --git a/lib/archiveutils.cpp b/lib/archiveutils.cpp index 4a498abd..521d05c3 100644 --- a/lib/archiveutils.cpp +++ b/lib/archiveutils.cpp @@ -1,85 +1,85 @@ // vim: set tabstop=4 shiftwidth=4 expandtab /* Gwenview - A simple image viewer for KDE Copyright 2000-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 "archiveutils.h" // KDE #include #include #include #include namespace Gwenview { namespace ArchiveUtils { bool fileItemIsArchive(const KFileItem& item) { QMimeType mimeType = item.determineMimeType(); if (!mimeType.isValid()) { qWarning() << "determineMimeType() returned a null pointer"; return false; } return !ArchiveUtils::protocolForMimeType(mimeType.name()).isEmpty(); } bool fileItemIsDirOrArchive(const KFileItem& item) { return item.isDir() || fileItemIsArchive(item); } QString protocolForMimeType(const QString& mimeType) { static QHash cache; QHash::ConstIterator it = cache.constFind(mimeType); if (it != cache.constEnd()) { return it.value(); } - if (mimeType == "image/svg+xml-compressed") { + if (mimeType == QLatin1String("image/svg+xml-compressed")) { // We don't want .svgz to be considered as archives because QtSvg knows // how to decode gzip-ed svg files cache.insert(mimeType, QString()); return QString(); } QString protocol = KProtocolManager::protocolForArchiveMimetype(mimeType); if (protocol.isEmpty()) { // No protocol, try with mimeType parents. This is useful for .cbz for // example QMimeType mime = QMimeDatabase().mimeTypeForName(mimeType); for(const QString & parentMimeType : mime.allAncestors()) { protocol = KProtocolManager::protocolForArchiveMimetype(parentMimeType); if (!protocol.isEmpty()) { break; } } } cache.insert(mimeType, protocol); return protocol; } } // namespace ArchiveUtils } // namespace Gwenview diff --git a/lib/binder.h b/lib/binder.h index 9b1750b3..386f06c3 100644 --- a/lib/binder.h +++ b/lib/binder.h @@ -1,125 +1,125 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2009 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, Cambridge, MA 02110-1301, USA. */ #ifndef BINDER_H #define BINDER_H #include // Qt #include namespace Gwenview { /** * @internal * * Necessary helper class because a QObject class cannot be a template */ class GWENVIEWLIB_EXPORT BinderInternal : public QObject { Q_OBJECT public: explicit BinderInternal(QObject* parent); ~BinderInternal(); protected Q_SLOTS: virtual void callMethod() {} }; /** * The Binder and BinderRef classes make it possible to "connect" a * parameter-less signal with a slot which accepts one argument. The * argument must be known at connection time. * * Example: * * Assuming a class like this: * * class Receiver * { * public: * void doSomething(Param* p); * }; * * This code: * * Binder::bind(emitter, SIGNAL(somethingHappened()), receiver, &Receiver::doSomething, p) * * Will result in receiver->doSomething(p) being called when emitter emits * the somethingHappened() signal. * * Just like a regular QObject connection, the connection will last until * either emitter or receiver are deleted. * * Using this system avoids creating an helper slot and adding a member to * the Receiver class to store the argument of the method to call. * * To call a method which accept a pointer or a value argument, use Binder. * To call a method which accept a const reference argument, use BinderRef. * * Note: the method does not need to be a slot. */ template class BaseBinder : public BinderInternal { public: typedef void (Receiver::*Method)(MethodArg); static void bind(QObject* emitter, const char* signal, Receiver* receiver, Method method, MethodArg arg) { BaseBinder* binder = new BaseBinder(emitter); binder->mReceiver = receiver; binder->mMethod = method; binder->mArg = arg; QObject::connect(emitter, signal, binder, SLOT(callMethod())); QObject::connect(receiver, SIGNAL(destroyed(QObject*)), binder, SLOT(deleteLater())); } protected: - void callMethod() Q_DECL_OVERRIDE + void callMethod() override { (mReceiver->*mMethod)(mArg); } private: BaseBinder(QObject* emitter) : BinderInternal(emitter) , mReceiver(0) , mMethod(0) {} Receiver* mReceiver; Method mMethod; Arg mArg; }; template class Binder : public BaseBinder {}; template class BinderRef : public BaseBinder {}; } // namespace #endif /* BINDER_H */ diff --git a/lib/crop/cropimageoperation.cpp b/lib/crop/cropimageoperation.cpp index 128fd156..8a0197cc 100644 --- a/lib/crop/cropimageoperation.cpp +++ b/lib/crop/cropimageoperation.cpp @@ -1,95 +1,95 @@ // 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 "cropimageoperation.h" // Qt #include // KDE #include #include // Local #include "document/document.h" #include "document/documentjob.h" #include "document/abstractdocumenteditor.h" namespace Gwenview { class CropJob : public ThreadedDocumentJob { public: CropJob(const QRect& rect) : mRect(rect) {} - void threadedStart() Q_DECL_OVERRIDE + void threadedStart() override { if (!checkDocumentEditor()) { return; } const QImage src = document()->image(); const QImage dst = src.copy(mRect); document()->editor()->setImage(dst); setError(NoError); } private: QRect mRect; }; struct CropImageOperationPrivate { QRect mRect; QImage mOriginalImage; }; CropImageOperation::CropImageOperation(const QRect& rect) : d(new CropImageOperationPrivate) { d->mRect = rect; setText(i18n("Crop")); } CropImageOperation::~CropImageOperation() { delete d; } void CropImageOperation::redo() { d->mOriginalImage = document()->image(); redoAsDocumentJob(new CropJob(d->mRect)); } void CropImageOperation::undo() { if (!document()->editor()) { qWarning() << "!document->editor()"; return; } document()->editor()->setImage(d->mOriginalImage); finish(true); } } // namespace diff --git a/lib/crop/cropimageoperation.h b/lib/crop/cropimageoperation.h index c7e4f50a..df6bdf12 100644 --- a/lib/crop/cropimageoperation.h +++ b/lib/crop/cropimageoperation.h @@ -1,54 +1,54 @@ // 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. */ #ifndef CROPIMAGEOPERATION_H #define CROPIMAGEOPERATION_H #include // Qt // KDE // Local #include class QRect; namespace Gwenview { struct CropImageOperationPrivate; class GWENVIEWLIB_EXPORT CropImageOperation : public AbstractImageOperation { public: CropImageOperation(const QRect&); ~CropImageOperation(); - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; + void redo() override; + void undo() override; private: CropImageOperationPrivate* const d; }; } // namespace #endif /* CROPIMAGEOPERATION_H */ diff --git a/lib/crop/croptool.h b/lib/crop/croptool.h index a39291a9..7c5a70c4 100644 --- a/lib/crop/croptool.h +++ b/lib/crop/croptool.h @@ -1,80 +1,80 @@ // 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. */ #ifndef CROPTOOL_H #define CROPTOOL_H #include // Qt // KDE // Local #include class QRect; namespace Gwenview { class AbstractImageOperation; struct CropToolPrivate; class GWENVIEWLIB_EXPORT CropTool : public AbstractRasterImageViewTool { Q_OBJECT public: CropTool(RasterImageView* parent); - ~CropTool() Q_DECL_OVERRIDE; + ~CropTool() override; void setCropRatio(double ratio); void setRect(const QRect&); QRect rect() const; - void paint(QPainter*) Q_DECL_OVERRIDE; + void paint(QPainter*) override; - void mousePressEvent(QGraphicsSceneMouseEvent*) Q_DECL_OVERRIDE; - void mouseMoveEvent(QGraphicsSceneMouseEvent*) Q_DECL_OVERRIDE; - void mouseReleaseEvent(QGraphicsSceneMouseEvent*) Q_DECL_OVERRIDE; - void hoverMoveEvent(QGraphicsSceneHoverEvent*) Q_DECL_OVERRIDE; - void keyPressEvent(QKeyEvent*) Q_DECL_OVERRIDE; + void mousePressEvent(QGraphicsSceneMouseEvent*) override; + void mouseMoveEvent(QGraphicsSceneMouseEvent*) override; + void mouseReleaseEvent(QGraphicsSceneMouseEvent*) override; + void hoverMoveEvent(QGraphicsSceneHoverEvent*) override; + void keyPressEvent(QKeyEvent*) override; - void toolActivated() Q_DECL_OVERRIDE; - void toolDeactivated() Q_DECL_OVERRIDE; + void toolActivated() override; + void toolDeactivated() override; - QWidget* widget() const Q_DECL_OVERRIDE; + QWidget* widget() const override; Q_SIGNALS: void rectUpdated(const QRect&); void done(); void imageOperationRequested(AbstractImageOperation*); private Q_SLOTS: void slotCropRequested(); private: CropToolPrivate* const d; }; } // namespace #endif /* CROPTOOL_H */ diff --git a/lib/crop/cropwidget.cpp b/lib/crop/cropwidget.cpp index 9d2d1758..f991dcab 100644 --- a/lib/crop/cropwidget.cpp +++ b/lib/crop/cropwidget.cpp @@ -1,449 +1,449 @@ // 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 // Qt #include #include #include #include #include #include // KDE #include // Local #include #include #include "croptool.h" #include "signalblocker.h" #include "ui_cropwidget.h" #include "cropwidget.h" namespace Gwenview { // Euclidean algorithm to compute the greatest common divisor of two integers. // Found at: // http://en.wikipedia.org/wiki/Euclidean_algorithm static int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); } static QSize ratio(const QSize &size) { const int divisor = gcd(size.width(), size.height()); return size / divisor; } struct CropWidgetPrivate : public Ui_CropWidget { CropWidget* q; Document::Ptr mDocument; CropTool* mCropTool; bool mUpdatingFromCropTool; int mCurrentImageComboBoxIndex; int mCropRatioComboBoxCurrentIndex; bool ratioIsConstrained() const { return cropRatio() > 0; } QSizeF chosenRatio() const { // A size of 0 represents no ratio, i.e. the combobox is empty if (ratioComboBox->currentText().isEmpty()) { return QSizeF(0, 0); } // A preset ratio is selected const int index = ratioComboBox->currentIndex(); if (index != -1 && ratioComboBox->currentText() == ratioComboBox->itemText(index)) { return ratioComboBox->currentData().toSizeF(); } // A custom ratio has been entered, extract ratio from the text // If invalid, return zero size instead - const QStringList lst = ratioComboBox->currentText().split(':'); + const QStringList lst = ratioComboBox->currentText().split(QLatin1Char(':')); if (lst.size() != 2) { return QSizeF(0, 0); } bool ok; const double width = lst[0].toDouble(&ok); if (!ok) { return QSizeF(0, 0); } const double height = lst[1].toDouble(&ok); if (!ok) { return QSizeF(0, 0); } // Valid custom value return QSizeF(width, height); } void setChosenRatio(QSizeF size) const { // Size matches preset ratio, let's set the combobox to that const int index = ratioComboBox->findData(size); if (index >= 0) { ratioComboBox->setCurrentIndex(index); return; } // Deselect whatever was selected if anything ratioComboBox->setCurrentIndex(-1); // If size is 0 (represents blank combobox, i.e., unrestricted) if (size.isEmpty()) { ratioComboBox->clearEditText(); return; } // Size must be custom ratio, convert to text and add to combobox - QString ratioString = QString("%1:%2").arg(size.width()).arg(size.height()); + QString ratioString = QStringLiteral("%1:%2").arg(size.width()).arg(size.height()); ratioComboBox->setCurrentText(ratioString); } double cropRatio() const { if (q->advancedSettingsEnabled()) { QSizeF size = chosenRatio(); if (size.isEmpty()) { return 0; } return size.height() / size.width(); } if (q->preserveAspectRatio()) { QSizeF size = ratio(mDocument->size()); return size.height() / size.width(); } return 0; } void addRatioToComboBox(const QSizeF& size, const QString& label = QString()) { QString text = label.isEmpty() - ? QString("%1:%2").arg(size.width()).arg(size.height()) + ? QStringLiteral("%1:%2").arg(size.width()).arg(size.height()) : label; ratioComboBox->addItem(text, QVariant(size)); } void addSectionHeaderToComboBox(const QString& title) { // Insert a line ratioComboBox->insertSeparator(ratioComboBox->count()); // Insert our section header // This header is made of a separator with a text. We reset // Qt::AccessibleDescriptionRole to the header text otherwise QComboBox // delegate will draw a separator line instead of our text. int index = ratioComboBox->count(); ratioComboBox->insertSeparator(index); ratioComboBox->setItemText(index, title); ratioComboBox->setItemData(index, title, Qt::AccessibleDescriptionRole); ratioComboBox->setItemData(index, Qt::AlignHCenter, Qt::TextAlignmentRole); } void initRatioComboBox() { QList ratioList; const qreal sqrt2 = qSqrt(2.); ratioList << QSizeF(16, 9) << QSizeF(7, 5) << QSizeF(3, 2) << QSizeF(4, 3) << QSizeF(5, 4); addRatioToComboBox(ratio(mDocument->size()), i18n("Current Image")); mCurrentImageComboBoxIndex = ratioComboBox->count() - 1; // We need to refer to this ratio later addRatioToComboBox(QSizeF(1, 1), i18n("Square")); addRatioToComboBox(ratio(QApplication::desktop()->screenGeometry().size()), i18n("This Screen")); addSectionHeaderToComboBox(i18n("Landscape")); Q_FOREACH(const QSizeF& size, ratioList) { addRatioToComboBox(size); } addRatioToComboBox(QSizeF(sqrt2, 1), i18n("ISO (A4, A3...)")); addRatioToComboBox(QSizeF(11, 8.5), i18n("US Letter")); addSectionHeaderToComboBox(i18n("Portrait")); Q_FOREACH(QSizeF size, ratioList) { size.transpose(); addRatioToComboBox(size); } addRatioToComboBox(QSizeF(1, sqrt2), i18n("ISO (A4, A3...)")); addRatioToComboBox(QSizeF(8.5, 11), i18n("US Letter")); ratioComboBox->setMaxVisibleItems(ratioComboBox->count()); ratioComboBox->clearEditText(); QLineEdit* edit = qobject_cast(ratioComboBox->lineEdit()); Q_ASSERT(edit); // Do not use i18n("%1:%2") because ':' should not be translated, it is // used to parse the ratio string. - edit->setPlaceholderText(QString("%1:%2").arg(i18n("Width")).arg(i18n("Height"))); + edit->setPlaceholderText(QStringLiteral("%1:%2").arg(i18n("Width")).arg(i18n("Height"))); // Enable clear button edit->setClearButtonEnabled(true); // Must manually adjust minimum width because the auto size adjustment doesn't take the // clear button into account const int width = ratioComboBox->minimumSizeHint().width(); ratioComboBox->setMinimumWidth(width + 24); ratioComboBox->setCurrentIndex(-1); } QRect cropRect() const { QRect rect( leftSpinBox->value(), topSpinBox->value(), widthSpinBox->value(), heightSpinBox->value() ); return rect; } void initSpinBoxes() { QSize size = mDocument->size(); leftSpinBox->setMaximum(size.width()); widthSpinBox->setMaximum(size.width()); topSpinBox->setMaximum(size.height()); heightSpinBox->setMaximum(size.height()); } void initDialogButtonBox() { QPushButton* cropButton = dialogButtonBox->button(QDialogButtonBox::Ok); - cropButton->setIcon(QIcon::fromTheme("transform-crop-and-resize")); + cropButton->setIcon(QIcon::fromTheme(QStringLiteral("transform-crop-and-resize"))); cropButton->setText(i18n("Crop")); QObject::connect(dialogButtonBox, &QDialogButtonBox::accepted, q, &CropWidget::cropRequested); QObject::connect(dialogButtonBox, &QDialogButtonBox::rejected, q, &CropWidget::done); } }; CropWidget::CropWidget(QWidget* parent, RasterImageView* imageView, CropTool* cropTool) : QWidget(parent) , d(new CropWidgetPrivate) { setWindowFlags(Qt::Tool); d->q = this; d->mDocument = imageView->document(); d->mUpdatingFromCropTool = false; d->mCropTool = cropTool; d->setupUi(this); setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont)); layout()->setSizeConstraint(QLayout::SetFixedSize); connect(d->advancedCheckBox, &QCheckBox::toggled, this, &CropWidget::slotAdvancedCheckBoxToggled); d->advancedWidget->setVisible(false); d->advancedWidget->layout()->setMargin(0); connect(d->preserveAspectRatioCheckBox, &QCheckBox::toggled, this, &CropWidget::applyRatioConstraint); d->initRatioComboBox(); connect(d->mCropTool, &CropTool::rectUpdated, this, &CropWidget::setCropRect); connect(d->leftSpinBox, static_cast(&QSpinBox::valueChanged), this, &CropWidget::slotPositionChanged); connect(d->topSpinBox, static_cast(&QSpinBox::valueChanged), this, &CropWidget::slotPositionChanged); connect(d->widthSpinBox, static_cast(&QSpinBox::valueChanged), this, &CropWidget::slotWidthChanged); connect(d->heightSpinBox, static_cast(&QSpinBox::valueChanged), this, &CropWidget::slotHeightChanged); d->initDialogButtonBox(); // We need to listen for both signals because the combobox is multi-function: // Text Changed: required so that manual ratio entry is detected (index doesn't change) // Index Changed: required so that choosing an item with the same text is detected (e.g. going from US Letter portrait // to US Letter landscape) connect(d->ratioComboBox, &QComboBox::editTextChanged, this, &CropWidget::slotRatioComboBoxChanged); connect(d->ratioComboBox, static_cast(&QComboBox::currentIndexChanged), this, &CropWidget::slotRatioComboBoxChanged); // Don't do this before signals are connected, otherwise the tool won't get // initialized d->initSpinBoxes(); setCropRect(d->mCropTool->rect()); } CropWidget::~CropWidget() { delete d; } void CropWidget::setAdvancedSettingsEnabled(bool enable) { d->advancedCheckBox->setChecked(enable); } bool CropWidget::advancedSettingsEnabled() const { return d->advancedCheckBox->isChecked(); } void CropWidget::setPreserveAspectRatio(bool preserve) { d->preserveAspectRatioCheckBox->setChecked(preserve); } bool CropWidget::preserveAspectRatio() const { return d->preserveAspectRatioCheckBox->isChecked(); } void CropWidget::setCropRatio(QSizeF size) { d->setChosenRatio(size); } QSizeF CropWidget::cropRatio() const { return d->chosenRatio(); } void CropWidget::setCropRatioIndex(int index) { d->ratioComboBox->setCurrentIndex(index); } int CropWidget::cropRatioIndex() const { return d->mCropRatioComboBoxCurrentIndex; } void CropWidget::setCropRect(const QRect& rect) { d->mUpdatingFromCropTool = true; d->leftSpinBox->setValue(rect.left()); d->topSpinBox->setValue(rect.top()); d->widthSpinBox->setValue(rect.width()); d->heightSpinBox->setValue(rect.height()); d->mUpdatingFromCropTool = false; } void CropWidget::slotPositionChanged() { const QSize size = d->mDocument->size(); d->widthSpinBox->setMaximum(size.width() - d->leftSpinBox->value()); d->heightSpinBox->setMaximum(size.height() - d->topSpinBox->value()); if (d->mUpdatingFromCropTool) { return; } d->mCropTool->setRect(d->cropRect()); } void CropWidget::slotWidthChanged() { d->leftSpinBox->setMaximum(d->mDocument->width() - d->widthSpinBox->value()); if (d->mUpdatingFromCropTool) { return; } if (d->ratioIsConstrained()) { int height = int(d->widthSpinBox->value() * d->cropRatio()); d->heightSpinBox->setValue(height); } d->mCropTool->setRect(d->cropRect()); } void CropWidget::slotHeightChanged() { d->topSpinBox->setMaximum(d->mDocument->height() - d->heightSpinBox->value()); if (d->mUpdatingFromCropTool) { return; } if (d->ratioIsConstrained()) { int width = int(d->heightSpinBox->value() / d->cropRatio()); d->widthSpinBox->setValue(width); } d->mCropTool->setRect(d->cropRect()); } void CropWidget::applyRatioConstraint() { double ratio = d->cropRatio(); d->mCropTool->setCropRatio(ratio); if (!d->ratioIsConstrained()) { return; } QRect rect = d->cropRect(); rect.setHeight(int(rect.width() * ratio)); d->mCropTool->setRect(rect); } void CropWidget::slotAdvancedCheckBoxToggled(bool checked) { d->advancedWidget->setVisible(checked); d->preserveAspectRatioCheckBox->setVisible(!checked); applyRatioConstraint(); } void CropWidget::slotRatioComboBoxChanged() { const QString text = d->ratioComboBox->currentText(); // If text cleared, clear the current item as well if (text.isEmpty()) { d->ratioComboBox->setCurrentIndex(-1); } // We want to keep track of the selected ratio, including when the user has entered a custom ratio // or cleared the text. We can't simply use currentIndex() because this stays >= 0 when the user manually // enters text. We also can't set the current index to -1 when there is no match like above because that // interferes when manually entering text. // Furthermore, since there can be duplicate text items, we can't rely on findText() as it will stop on // the first match it finds. Therefore we must check if there's a match, and if so, get the index directly. if (d->ratioComboBox->findText(text) >= 0) { d->mCropRatioComboBoxCurrentIndex = d->ratioComboBox->currentIndex(); } else { d->mCropRatioComboBoxCurrentIndex = -1; } applyRatioConstraint(); } void CropWidget::updateCropRatio() { // First we need to re-calculate the "Current Image" ratio in case the user rotated the image d->ratioComboBox->setItemData(d->mCurrentImageComboBoxIndex, QVariant(ratio(d->mDocument->size()))); // Always re-apply the constraint, even though we only need to when the user has "Current Image" // selected or the "Preserve aspect ratio" checked, since there's no harm applyRatioConstraint(); // If the ratio is unrestricted, calling applyRatioConstraint doesn't update the rect, so we call // this manually to make sure the rect is adjusted to fit within the image d->mCropTool->setRect(d->mCropTool->rect()); } } // namespace diff --git a/lib/datewidget.cpp b/lib/datewidget.cpp index dec7e02f..53faf6a3 100644 --- a/lib/datewidget.cpp +++ b/lib/datewidget.cpp @@ -1,145 +1,145 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 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, Cambridge, MA 02110-1301, USA. */ // Self #include "datewidget.h" // Qt #include #include #include // KDE #include #include // Local #include namespace Gwenview { struct DateWidgetPrivate { DateWidget* q; QDate mDate; KDatePicker* mDatePicker; StatusBarToolButton* mPreviousButton; StatusBarToolButton* mDateButton; StatusBarToolButton* mNextButton; void setupDatePicker() { mDatePicker = new KDatePicker; /* Use Qt::Tool instead of Qt::Window so that the bubble does not appear in the task bar */ //mDatePicker->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint); mDatePicker->setWindowFlags(Qt::Popup); mDatePicker->hide(); mDatePicker->setFrameStyle(QFrame::StyledPanel | QFrame::Raised); QObject::connect(mDatePicker, &KDatePicker::dateEntered, q, &DateWidget::slotDatePickerModified); QObject::connect(mDatePicker, &KDatePicker::dateSelected, q, &DateWidget::slotDatePickerModified); } void updateButton() { mDateButton->setText(QLocale().toString(mDate, QLocale::ShortFormat)); } void adjustDate(int delta) { mDate = mDate.addDays(delta); updateButton(); q->dateChanged(mDate); } }; DateWidget::DateWidget(QWidget* parent) : QWidget(parent) , d(new DateWidgetPrivate) { d->q = this; d->setupDatePicker(); d->mPreviousButton = new StatusBarToolButton; d->mPreviousButton->setGroupPosition(StatusBarToolButton::GroupLeft); // FIXME: RTL - d->mPreviousButton->setIcon(QIcon::fromTheme("go-previous")); + d->mPreviousButton->setIcon(QIcon::fromTheme(QStringLiteral("go-previous"))); connect(d->mPreviousButton, &StatusBarToolButton::clicked, this, &DateWidget::goToPrevious); d->mDateButton = new StatusBarToolButton; d->mDateButton->setGroupPosition(StatusBarToolButton::GroupCenter); connect(d->mDateButton, &StatusBarToolButton::clicked, this, &DateWidget::showDatePicker); d->mNextButton = new StatusBarToolButton; d->mNextButton->setGroupPosition(StatusBarToolButton::GroupRight); - d->mNextButton->setIcon(QIcon::fromTheme("go-next")); + d->mNextButton->setIcon(QIcon::fromTheme(QStringLiteral("go-next"))); connect(d->mNextButton, &StatusBarToolButton::clicked, this, &DateWidget::goToNext); QHBoxLayout* layout = new QHBoxLayout(this); layout->setMargin(0); layout->setSpacing(0); layout->addWidget(d->mPreviousButton); layout->addWidget(d->mDateButton); layout->addWidget(d->mNextButton); } DateWidget::~DateWidget() { delete d->mDatePicker; delete d; } QDate DateWidget::date() const { return d->mDate; } void DateWidget::showDatePicker() { d->mDatePicker->setDate(d->mDate); d->mDatePicker->adjustSize(); const QPoint pos = mapToGlobal(QPoint(0, -d->mDatePicker->height())); d->mDatePicker->move(pos); d->mDatePicker->show(); } void DateWidget::slotDatePickerModified(const QDate& date) { d->mDatePicker->hide(); d->mDate = date; emit dateChanged(date); d->updateButton(); } void DateWidget::goToPrevious() { d->adjustDate(-1); } void DateWidget::goToNext() { d->adjustDate(1); } } // namespace diff --git a/lib/disabledactionshortcutmonitor.h b/lib/disabledactionshortcutmonitor.h index fc320b29..91651463 100644 --- a/lib/disabledactionshortcutmonitor.h +++ b/lib/disabledactionshortcutmonitor.h @@ -1,63 +1,63 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2014 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, see . */ #ifndef DISABLEDACTIONSHORTCUTMONITOR_H #define DISABLEDACTIONSHORTCUTMONITOR_H #include // Local // KDE // Qt #include class QAction; namespace Gwenview { class DisabledActionShortcutMonitorPrivate; /** * Monitors an action and emits a signal if one tries to trigger the action * using its shortcut while it is disabled. */ class GWENVIEWLIB_EXPORT DisabledActionShortcutMonitor : public QObject { Q_OBJECT public: /** * parent must be a widget because we need to create a QShortcut */ DisabledActionShortcutMonitor(QAction* action, QWidget* parent); - ~DisabledActionShortcutMonitor() Q_DECL_OVERRIDE; + ~DisabledActionShortcutMonitor() override; Q_SIGNALS: void activated(); protected: - bool eventFilter(QObject* object, QEvent* event) Q_DECL_OVERRIDE; + bool eventFilter(QObject* object, QEvent* event) override; private: DisabledActionShortcutMonitorPrivate* const d; }; } // namespace #endif /* DISABLEDACTIONSHORTCUTMONITOR_H */ diff --git a/lib/document/animateddocumentloadedimpl.h b/lib/document/animateddocumentloadedimpl.h index ab77229f..43dadba6 100644 --- a/lib/document/animateddocumentloadedimpl.h +++ b/lib/document/animateddocumentloadedimpl.h @@ -1,58 +1,58 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 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, Cambridge, MA 02110-1301, USA. */ #ifndef ANIMATEDDOCUMENTLOADEDIMPL_H #define ANIMATEDDOCUMENTLOADEDIMPL_H // Qt // KDE // Local #include namespace Gwenview { struct AnimatedDocumentLoadedImplPrivate; class AnimatedDocumentLoadedImpl : public AbstractDocumentImpl { Q_OBJECT public: AnimatedDocumentLoadedImpl(Document*, const QByteArray&); - ~AnimatedDocumentLoadedImpl() Q_DECL_OVERRIDE; - - void init() Q_DECL_OVERRIDE; - Document::LoadingState loadingState() const Q_DECL_OVERRIDE; - QByteArray rawData() const Q_DECL_OVERRIDE; - bool isAnimated() const Q_DECL_OVERRIDE; - void startAnimation() Q_DECL_OVERRIDE; - void stopAnimation() Q_DECL_OVERRIDE; + ~AnimatedDocumentLoadedImpl() override; + + void init() override; + Document::LoadingState loadingState() const override; + QByteArray rawData() const override; + bool isAnimated() const override; + void startAnimation() override; + void stopAnimation() override; private Q_SLOTS: void slotFrameChanged(int frameNumber); private: AnimatedDocumentLoadedImplPrivate* const d; }; } // namespace #endif /* ANIMATEDDOCUMENTLOADEDIMPL_H */ diff --git a/lib/document/document_p.h b/lib/document/document_p.h index 00c6f7ff..78e0ac6e 100644 --- a/lib/document/document_p.h +++ b/lib/document/document_p.h @@ -1,88 +1,88 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2013 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, Cambridge, MA 02110-1301, USA. */ #ifndef DOCUMENT_P_H #define DOCUMENT_P_H // Local #include #include // KDE #include // Qt #include #include #include #include namespace Gwenview { typedef QQueue DocumentJobQueue; struct DocumentPrivate { Document* q; AbstractDocumentImpl* mImpl; QUrl mUrl; bool mKeepRawData; QPointer mCurrentJob; DocumentJobQueue mJobQueue; /** * @defgroup imagedata should be reset in reload() * @{ */ QSize mSize; QImage mImage; QMap mDownSampledImageMap; Exiv2::Image::AutoPtr mExiv2Image; MimeTypeUtils::Kind mKind; QByteArray mFormat; ImageMetaInfoModel mImageMetaInfoModel; QUndoStack mUndoStack; QString mErrorString; Cms::Profile::Ptr mCmsProfile; /** @} */ void scheduleImageLoading(int invertedZoom); void scheduleImageDownSampling(int invertedZoom); void downSampleImage(int invertedZoom); }; class DownSamplingJob : public DocumentJob { Q_OBJECT public: DownSamplingJob(int invertedZoom) : mInvertedZoom(invertedZoom) {} - void doStart() Q_DECL_OVERRIDE; + void doStart() override; int mInvertedZoom; }; } // namespace #endif /* DOCUMENT_P_H */ diff --git a/lib/document/documentjob.h b/lib/document/documentjob.h index 3bd8989b..7c22324d 100644 --- a/lib/document/documentjob.h +++ b/lib/document/documentjob.h @@ -1,116 +1,116 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2010 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, Cambridge, MA 02110-1301, USA. */ #ifndef DOCUMENTJOB_H #define DOCUMENTJOB_H #include // Qt // KDE #include // Local #include namespace Gwenview { struct DocumentJobPrivate; /** * Represent an asynchronous task to be executed on a document * The task must be created on the heap and passed to an instance of Document * via Document::enqueueJob() * * The task behavior must be implemented in run() * * Tasks are always started from the GUI thread, and are never parallelized. * You can of course use threading inside your task implementation to speed it * up. */ class GWENVIEWLIB_EXPORT DocumentJob : public KCompositeJob { Q_OBJECT public: enum { NoDocumentEditorError = UserDefinedError + 1 }; DocumentJob(); - ~DocumentJob() Q_DECL_OVERRIDE; + ~DocumentJob() override; Document::Ptr document() const; - void start() Q_DECL_OVERRIDE; + void start() override; protected Q_SLOTS: /** * Implement this method to provide the task behavior. * You *must* emit the result() signal when your work is finished, but it * does not have to be finished when run() returns. * If you are not emitting it from the GUI thread, then use a queued * connection to emit it. */ virtual void doStart() = 0; /** * slot-ification of emitResult() */ void emitResult() { KJob::emitResult(); } protected: /** * Convenience method which checks document()->editor() is valid * and set the job error properties if it's not. * @return true if the editor is valid. */ bool checkDocumentEditor(); private: void setDocument(const Document::Ptr&); DocumentJobPrivate* const d; friend class Document; }; /** * A document job whose action is started in a separate thread */ class ThreadedDocumentJob : public DocumentJob { public: /** * Must be reimplemented to apply the action to the document. * This method is never called from the GUI thread. */ virtual void threadedStart() = 0; protected: - void doStart() Q_DECL_OVERRIDE; + void doStart() override; }; } // namespace #endif /* DOCUMENTJOB_H */ diff --git a/lib/document/documentloadedimpl.h b/lib/document/documentloadedimpl.h index 6cf17f6a..c64299e2 100644 --- a/lib/document/documentloadedimpl.h +++ b/lib/document/documentloadedimpl.h @@ -1,76 +1,76 @@ // 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. */ #ifndef DOCUMENTLOADEDIMPL_H #define DOCUMENTLOADEDIMPL_H // Qt // KDE // Local #include #include class QByteArray; class QIODevice; class QUrl; namespace Gwenview { struct DocumentLoadedImplPrivate; class DocumentLoadedImpl : public AbstractDocumentImpl, protected AbstractDocumentEditor { Q_OBJECT public: /** * @param quietInit set to true if init() should not emit any signal */ DocumentLoadedImpl(Document*, const QByteArray&, bool quietInit = false); - ~DocumentLoadedImpl() Q_DECL_OVERRIDE; + ~DocumentLoadedImpl() override; // AbstractDocumentImpl - void init() Q_DECL_OVERRIDE; - Document::LoadingState loadingState() const Q_DECL_OVERRIDE; - DocumentJob* save(const QUrl&, const QByteArray& format) Q_DECL_OVERRIDE; - AbstractDocumentEditor* editor() Q_DECL_OVERRIDE; - QByteArray rawData() const Q_DECL_OVERRIDE; - bool isEditable() const Q_DECL_OVERRIDE; + void init() override; + Document::LoadingState loadingState() const override; + DocumentJob* save(const QUrl&, const QByteArray& format) override; + AbstractDocumentEditor* editor() override; + QByteArray rawData() const override; + bool isEditable() const override; // protected: virtual bool saveInternal(QIODevice* device, const QByteArray& format); // AbstractDocumentEditor - void setImage(const QImage&) Q_DECL_OVERRIDE; - void applyTransformation(Orientation orientation) Q_DECL_OVERRIDE; + void setImage(const QImage&) override; + void applyTransformation(Orientation orientation) override; // private: DocumentLoadedImplPrivate* const d; friend class SaveJob; }; } // namespace #endif /* DOCUMENTLOADEDIMPL_H */ diff --git a/lib/document/emptydocumentimpl.h b/lib/document/emptydocumentimpl.h index 155e3a16..c34984bd 100644 --- a/lib/document/emptydocumentimpl.h +++ b/lib/document/emptydocumentimpl.h @@ -1,46 +1,46 @@ // 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. */ #ifndef EMPTYDOCUMENTIMPL_H #define EMPTYDOCUMENTIMPL_H // Qt // KDE // Local #include namespace Gwenview { class EmptyDocumentImpl : public AbstractDocumentImpl { public: EmptyDocumentImpl(Document*); - ~EmptyDocumentImpl() Q_DECL_OVERRIDE; + ~EmptyDocumentImpl() override; - void init() Q_DECL_OVERRIDE; - Document::LoadingState loadingState() const Q_DECL_OVERRIDE; + void init() override; + Document::LoadingState loadingState() const override; }; } // namespace #endif /* EMPTYDOCUMENTIMPL_H */ diff --git a/lib/document/jpegdocumentloadedimpl.h b/lib/document/jpegdocumentloadedimpl.h index 1ed96a57..dcf779ea 100644 --- a/lib/document/jpegdocumentloadedimpl.h +++ b/lib/document/jpegdocumentloadedimpl.h @@ -1,61 +1,61 @@ // 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. */ #ifndef JPEGDOCUMENTLOADEDIMPL_H #define JPEGDOCUMENTLOADEDIMPL_H // Qt // KDE // Local #include class QByteArray; namespace Gwenview { class JpegContent; struct JpegDocumentLoadedImplPrivate; class JpegDocumentLoadedImpl : public DocumentLoadedImpl { Q_OBJECT public: JpegDocumentLoadedImpl(Document*, JpegContent*); - ~JpegDocumentLoadedImpl() Q_DECL_OVERRIDE; - QByteArray rawData() const Q_DECL_OVERRIDE; + ~JpegDocumentLoadedImpl() override; + QByteArray rawData() const override; protected: - bool saveInternal(QIODevice* device, const QByteArray& format) Q_DECL_OVERRIDE; + bool saveInternal(QIODevice* device, const QByteArray& format) override; // AbstractDocumentEditor - void setImage(const QImage&) Q_DECL_OVERRIDE; - void applyTransformation(Orientation orientation) Q_DECL_OVERRIDE; + void setImage(const QImage&) override; + void applyTransformation(Orientation orientation) override; // private: JpegDocumentLoadedImplPrivate* const d; }; } // namespace #endif /* JPEGDOCUMENTLOADEDIMPL_H */ diff --git a/lib/document/loadingdocumentimpl.cpp b/lib/document/loadingdocumentimpl.cpp index 713ef32c..238699de 100644 --- a/lib/document/loadingdocumentimpl.cpp +++ b/lib/document/loadingdocumentimpl.cpp @@ -1,554 +1,554 @@ // 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 "loadingdocumentimpl.h" // STL #include // Qt #include #include #include #include #include #include #include #include #include #include #include // KDE #include #include #include #include #ifdef KDCRAW_FOUND #include #endif // Local #include "animateddocumentloadedimpl.h" #include "cms/cmsprofile.h" #include "document.h" #include "documentloadedimpl.h" #include "emptydocumentimpl.h" #include "exiv2imageloader.h" #include "gvdebug.h" #include "imageutils.h" #include "jpegcontent.h" #include "jpegdocumentloadedimpl.h" #include "orientation.h" #include "svgdocumentloadedimpl.h" #include "urlutils.h" #include "videodocumentloadedimpl.h" #include "gwenviewconfig.h" namespace Gwenview { #undef ENABLE_LOG #undef LOG //#define ENABLE_LOG #ifdef ENABLE_LOG #define LOG(x) //qDebug() << x #else #define LOG(x) ; #endif const int MIN_PREV_SIZE = 1000; const int HEADER_SIZE = 256; struct LoadingDocumentImplPrivate { LoadingDocumentImpl* q; QPointer mTransferJob; QFuture mMetaInfoFuture; QFutureWatcher mMetaInfoFutureWatcher; QFuture mImageDataFuture; QFutureWatcher mImageDataFutureWatcher; // If != 0, this means we need to load an image at zoom = // 1/mImageDataInvertedZoom int mImageDataInvertedZoom; bool mMetaInfoLoaded; bool mAnimated; bool mDownSampledImageLoaded; QByteArray mFormatHint; QByteArray mData; QByteArray mFormat; QSize mImageSize; Exiv2::Image::AutoPtr mExiv2Image; std::unique_ptr mJpegContent; QImage mImage; Cms::Profile::Ptr mCmsProfile; /** * Determine kind of document and switch to an implementation if it is not * necessary to download more data. * @return true if switched to another implementation. */ bool determineKind() { QString mimeType; const QUrl &url = q->document()->url(); QMimeDatabase db; if (KProtocolInfo::determineMimetypeFromExtension(url.scheme())) { mimeType = db.mimeTypeForFileNameAndData(url.fileName(), mData).name(); } else { mimeType = db.mimeTypeForData(mData).name(); } MimeTypeUtils::Kind kind = MimeTypeUtils::mimeTypeKind(mimeType); LOG("mimeType:" << mimeType); LOG("kind:" << kind); q->setDocumentKind(kind); switch (kind) { case MimeTypeUtils::KIND_RASTER_IMAGE: case MimeTypeUtils::KIND_SVG_IMAGE: return false; case MimeTypeUtils::KIND_VIDEO: q->switchToImpl(new VideoDocumentLoadedImpl(q->document())); return true; default: q->setDocumentErrorString( i18nc("@info", "Gwenview cannot display documents of type %1.", mimeType) ); emit q->loadingFailed(); q->switchToImpl(new EmptyDocumentImpl(q->document())); return true; } } void startLoading() { Q_ASSERT(!mMetaInfoLoaded); switch (q->document()->kind()) { case MimeTypeUtils::KIND_RASTER_IMAGE: // The hint is used to: // - Speed up loadMetaInfo(): QImageReader will try to decode the // image using plugins matching this format first. // - Avoid breakage: Because of a bug in Qt TGA image plugin, some // PNG were incorrectly identified as PCX! See: // https://bugs.kde.org/show_bug.cgi?id=289819 // mFormatHint = q->document()->url().fileName() - .section('.', -1).toLocal8Bit().toLower(); + .section(QLatin1Char('.'), -1).toLocal8Bit().toLower(); mMetaInfoFuture = QtConcurrent::run(this, &LoadingDocumentImplPrivate::loadMetaInfo); mMetaInfoFutureWatcher.setFuture(mMetaInfoFuture); break; case MimeTypeUtils::KIND_SVG_IMAGE: q->switchToImpl(new SvgDocumentLoadedImpl(q->document(), mData)); break; case MimeTypeUtils::KIND_VIDEO: break; default: qWarning() << "We should not reach this point!"; break; } } void startImageDataLoading() { LOG(""); Q_ASSERT(mMetaInfoLoaded); Q_ASSERT(mImageDataInvertedZoom != 0); Q_ASSERT(!mImageDataFuture.isRunning()); mImageDataFuture = QtConcurrent::run(this, &LoadingDocumentImplPrivate::loadImageData); mImageDataFutureWatcher.setFuture(mImageDataFuture); } bool loadMetaInfo() { LOG("mFormatHint" << mFormatHint); QBuffer buffer; buffer.setBuffer(&mData); buffer.open(QIODevice::ReadOnly); #ifdef KDCRAW_FOUND - if (KDcrawIface::KDcraw::rawFilesList().contains(QString(mFormatHint))) { + if (KDcrawIface::KDcraw::rawFilesList().contains(QString::fromLatin1(mFormatHint))) { QByteArray previewData; // if the image is in format supported by dcraw, fetch its embedded preview mJpegContent.reset(new JpegContent()); // use KDcraw for getting the embedded preview // KDcraw functionality cloned locally (temp. solution) bool ret = KDcrawIface::KDcraw::loadEmbeddedPreview(previewData, buffer); QImage originalImage; if (!ret || !originalImage.loadFromData(previewData) || qMin(originalImage.width(), originalImage.height()) < MIN_PREV_SIZE) { // if the embedded preview loading failed or gets just a small image, load // half preview instead. That's slower but it works even for images containing // small (160x120px) or none embedded preview. if (!KDcrawIface::KDcraw::loadHalfPreview(previewData, buffer)) { qWarning() << "unable to get half preview for " << q->document()->url().fileName(); return false; } } buffer.close(); // now it's safe to replace mData with the jpeg data mData = previewData; // need to fill mFormat so gwenview can tell the type when trying to save mFormat = mFormatHint; } else { #else { #endif QImageReader reader(&buffer, mFormatHint); mImageSize = reader.size(); if (!reader.canRead()) { qWarning() << "QImageReader::read() using format hint" << mFormatHint << "failed:" << reader.errorString(); if (buffer.pos() != 0) { qWarning() << "A bad Qt image decoder moved the buffer to" << buffer.pos() << "in a call to canRead()! Rewinding."; buffer.seek(0); } reader.setFormat(QByteArray()); // Set buffer again, otherwise QImageReader won't restart from scratch reader.setDevice(&buffer); if (!reader.canRead()) { qWarning() << "QImageReader::read() without format hint failed:" << reader.errorString(); return false; } qWarning() << "Image format is actually" << reader.format() << "not" << mFormatHint; } mFormat = reader.format(); if (mFormat == "jpg") { // if mFormatHint was "jpg", then mFormat is "jpg", but the rest of // Gwenview code assumes JPEG images have "jpeg" format. mFormat = "jpeg"; } } LOG("mFormat" << mFormat); GV_RETURN_VALUE_IF_FAIL(!mFormat.isEmpty(), false); Exiv2ImageLoader loader; if (loader.load(mData)) { mExiv2Image = loader.popImage(); } if (mFormat == "jpeg" && mExiv2Image.get()) { mJpegContent.reset(new JpegContent()); } if (mJpegContent.get()) { if (!mJpegContent->loadFromData(mData, mExiv2Image.get()) && !mJpegContent->loadFromData(mData)) { qWarning() << "Unable to use preview of " << q->document()->url().fileName(); return false; } // Use the size from JpegContent, as its correctly transposed if the // image has been rotated mImageSize = mJpegContent->size(); mCmsProfile = Cms::Profile::loadFromExiv2Image(mExiv2Image.get()); } LOG("mImageSize" << mImageSize); if (!mCmsProfile) { mCmsProfile = Cms::Profile::loadFromImageData(mData, mFormat); } return true; } void loadImageData() { QBuffer buffer; buffer.setBuffer(&mData); buffer.open(QIODevice::ReadOnly); QImageReader reader(&buffer, mFormat); LOG("mImageDataInvertedZoom=" << mImageDataInvertedZoom); if (mImageSize.isValid() && mImageDataInvertedZoom != 1 && reader.supportsOption(QImageIOHandler::ScaledSize) ) { // Do not use mImageSize here: QImageReader needs a non-transposed // image size QSize size = reader.size() / mImageDataInvertedZoom; if (!size.isEmpty()) { LOG("Setting scaled size to" << size); reader.setScaledSize(size); } else { LOG("Not setting scaled size as it is empty" << size); } } bool ok = reader.read(&mImage); if (!ok) { LOG("QImageReader::read() failed"); return; } if (mJpegContent.get() && GwenviewConfig::applyExifOrientation()) { Gwenview::Orientation orientation = mJpegContent->orientation(); QMatrix matrix = ImageUtils::transformMatrix(orientation); mImage = mImage.transformed(matrix); } if (reader.supportsAnimation() && reader.nextImageDelay() > 0 // Assume delay == 0 <=> only one frame ) { /* * QImageReader is not really helpful to detect animated gif: * - QImageReader::imageCount() returns 0 * - QImageReader::nextImageDelay() may return something > 0 if the * image consists of only one frame but includes a "Graphic * Control Extension" (usually only present if we have an * animation) (Bug #185523) * * Decoding the next frame is the only reliable way I found to * detect an animated gif */ LOG("May be an animated image. delay:" << reader.nextImageDelay()); QImage nextImage; if (reader.read(&nextImage)) { LOG("Really an animated image (more than one frame)"); mAnimated = true; } else { qWarning() << q->document()->url() << "is not really an animated image (only one frame)"; } } } }; LoadingDocumentImpl::LoadingDocumentImpl(Document* document) : AbstractDocumentImpl(document) , d(new LoadingDocumentImplPrivate) { d->q = this; d->mMetaInfoLoaded = false; d->mAnimated = false; d->mDownSampledImageLoaded = false; d->mImageDataInvertedZoom = 0; connect(&d->mMetaInfoFutureWatcher, SIGNAL(finished()), SLOT(slotMetaInfoLoaded())); connect(&d->mImageDataFutureWatcher, SIGNAL(finished()), SLOT(slotImageLoaded())); } LoadingDocumentImpl::~LoadingDocumentImpl() { LOG(""); // Disconnect watchers to make sure they do not trigger further work d->mMetaInfoFutureWatcher.disconnect(); d->mImageDataFutureWatcher.disconnect(); d->mMetaInfoFutureWatcher.waitForFinished(); d->mImageDataFutureWatcher.waitForFinished(); if (d->mTransferJob) { d->mTransferJob->kill(); } delete d; } void LoadingDocumentImpl::init() { QUrl url = document()->url(); if (UrlUtils::urlIsFastLocalFile(url)) { // Load file content directly QFile file(url.toLocalFile()); if (!file.open(QIODevice::ReadOnly)) { setDocumentErrorString(i18nc("@info", "Could not open file %1", url.toLocalFile())); emit loadingFailed(); switchToImpl(new EmptyDocumentImpl(document())); return; } d->mData = file.read(HEADER_SIZE); if (d->determineKind()) { return; } d->mData += file.readAll(); d->startLoading(); } else { // Transfer file via KIO d->mTransferJob = KIO::get(document()->url(), KIO::NoReload, KIO::HideProgressInfo); connect(d->mTransferJob, SIGNAL(data(KIO::Job*,QByteArray)), SLOT(slotDataReceived(KIO::Job*,QByteArray))); connect(d->mTransferJob, SIGNAL(result(KJob*)), SLOT(slotTransferFinished(KJob*))); d->mTransferJob->start(); } } void LoadingDocumentImpl::loadImage(int invertedZoom) { if (d->mImageDataInvertedZoom == invertedZoom) { LOG("Already loading an image at invertedZoom=" << invertedZoom); return; } if (d->mImageDataInvertedZoom == 1) { LOG("Ignoring request: we are loading a full image"); return; } d->mImageDataFutureWatcher.waitForFinished(); d->mImageDataInvertedZoom = invertedZoom; if (d->mMetaInfoLoaded) { // Do not test on mMetaInfoFuture.isRunning() here: it might not have // started if we are downloading the image from a remote url d->startImageDataLoading(); } } void LoadingDocumentImpl::slotDataReceived(KIO::Job* job, const QByteArray& chunk) { d->mData.append(chunk); if (document()->kind() == MimeTypeUtils::KIND_UNKNOWN && d->mData.length() >= HEADER_SIZE) { if (d->determineKind()) { job->kill(); return; } } } void LoadingDocumentImpl::slotTransferFinished(KJob* job) { if (job->error()) { setDocumentErrorString(job->errorString()); emit loadingFailed(); switchToImpl(new EmptyDocumentImpl(document())); return; } d->startLoading(); } bool LoadingDocumentImpl::isEditable() const { return d->mDownSampledImageLoaded; } Document::LoadingState LoadingDocumentImpl::loadingState() const { if (!document()->image().isNull()) { return Document::Loaded; } else if (d->mMetaInfoLoaded) { return Document::MetaInfoLoaded; } else if (document()->kind() != MimeTypeUtils::KIND_UNKNOWN) { return Document::KindDetermined; } else { return Document::Loading; } } void LoadingDocumentImpl::slotMetaInfoLoaded() { LOG(""); Q_ASSERT(!d->mMetaInfoFuture.isRunning()); if (!d->mMetaInfoFuture.result()) { setDocumentErrorString( i18nc("@info", "Loading meta information failed.") ); emit loadingFailed(); switchToImpl(new EmptyDocumentImpl(document())); return; } setDocumentFormat(d->mFormat); setDocumentImageSize(d->mImageSize); setDocumentExiv2Image(d->mExiv2Image); setDocumentCmsProfile(d->mCmsProfile); d->mMetaInfoLoaded = true; emit metaInfoLoaded(); // Start image loading if necessary // We test if mImageDataFuture is not already running because code connected to // metaInfoLoaded() signal could have called loadImage() if (!d->mImageDataFuture.isRunning() && d->mImageDataInvertedZoom != 0) { d->startImageDataLoading(); } } void LoadingDocumentImpl::slotImageLoaded() { LOG(""); if (d->mImage.isNull()) { setDocumentErrorString( i18nc("@info", "Loading image failed.") ); emit loadingFailed(); switchToImpl(new EmptyDocumentImpl(document())); return; } if (d->mAnimated) { if (d->mImage.size() == d->mImageSize) { // We already decoded the first frame at the right size, let's show // it setDocumentImage(d->mImage); } switchToImpl(new AnimatedDocumentLoadedImpl( document(), d->mData)); return; } if (d->mImageDataInvertedZoom != 1 && d->mImage.size() != d->mImageSize) { LOG("Loaded a down sampled image"); d->mDownSampledImageLoaded = true; // We loaded a down sampled image setDocumentDownSampledImage(d->mImage, d->mImageDataInvertedZoom); return; } LOG("Loaded a full image"); setDocumentImage(d->mImage); DocumentLoadedImpl* impl; if (d->mJpegContent.get()) { impl = new JpegDocumentLoadedImpl( document(), d->mJpegContent.release()); } else { impl = new DocumentLoadedImpl( document(), d->mData); } switchToImpl(impl); } } // namespace diff --git a/lib/document/loadingdocumentimpl.h b/lib/document/loadingdocumentimpl.h index aa513944..0c141691 100644 --- a/lib/document/loadingdocumentimpl.h +++ b/lib/document/loadingdocumentimpl.h @@ -1,67 +1,67 @@ // 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. */ #ifndef LOADINGDOCUMENTIMPL_H #define LOADINGDOCUMENTIMPL_H // Qt // KDE // Local #include class KJob; namespace KIO { class Job; } namespace Gwenview { struct LoadingDocumentImplPrivate; class LoadingDocumentImpl : public AbstractDocumentImpl { Q_OBJECT public: LoadingDocumentImpl(Document*); - ~LoadingDocumentImpl() Q_DECL_OVERRIDE; + ~LoadingDocumentImpl() override; - void init() Q_DECL_OVERRIDE; - Document::LoadingState loadingState() const Q_DECL_OVERRIDE; - bool isEditable() const Q_DECL_OVERRIDE; + void init() override; + Document::LoadingState loadingState() const override; + bool isEditable() const override; void loadImage(int invertedZoom); private Q_SLOTS: void slotMetaInfoLoaded(); void slotImageLoaded(); void slotDataReceived(KIO::Job*, const QByteArray&); void slotTransferFinished(KJob*); private: LoadingDocumentImplPrivate* const d; friend struct LoadingDocumentImplPrivate; }; } // namespace #endif /* LOADINGDOCUMENTIMPL_H */ diff --git a/lib/document/loadingjob.h b/lib/document/loadingjob.h index 5eea48e3..2585f8e8 100644 --- a/lib/document/loadingjob.h +++ b/lib/document/loadingjob.h @@ -1,47 +1,47 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2010 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, Cambridge, MA 02110-1301, USA. */ #ifndef LOADINGJOB_H #define LOADINGJOB_H // Qt // KDE // Local #include namespace Gwenview { class LoadingJob : public DocumentJob { Q_OBJECT protected: - void doStart() Q_DECL_OVERRIDE; + void doStart() override; private Q_SLOTS: void slotLoaded(); void slotLoadingFailed(); }; } // namespace #endif /* LOADINGJOB_H */ diff --git a/lib/document/savejob.h b/lib/document/savejob.h index 3c246934..c134d48f 100644 --- a/lib/document/savejob.h +++ b/lib/document/savejob.h @@ -1,69 +1,69 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2010 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, Cambridge, MA 02110-1301, USA. */ #ifndef SAVEJOB_H #define SAVEJOB_H #include // Qt // KDE // Local #include class QByteArray; class QUrl; namespace Gwenview { class DocumentLoadedImpl; struct SaveJobPrivate; class GWENVIEWLIB_EXPORT SaveJob : public DocumentJob { Q_OBJECT public: SaveJob(DocumentLoadedImpl* impl, const QUrl &url, const QByteArray& format); - ~SaveJob() Q_DECL_OVERRIDE; + ~SaveJob() override; void saveInternal(); QUrl oldUrl() const; QUrl newUrl() const; protected Q_SLOTS: - void doStart() Q_DECL_OVERRIDE; - void slotResult(KJob*) Q_DECL_OVERRIDE; + void doStart() override; + void slotResult(KJob*) override; protected: - bool doKill() Q_DECL_OVERRIDE; + bool doKill() override; private Q_SLOTS: void finishSave(); private: SaveJobPrivate* const d; }; } // namespace #endif /* SAVEJOB_H */ diff --git a/lib/document/svgdocumentloadedimpl.h b/lib/document/svgdocumentloadedimpl.h index 1b4f7d8f..24e0844f 100644 --- a/lib/document/svgdocumentloadedimpl.h +++ b/lib/document/svgdocumentloadedimpl.h @@ -1,58 +1,58 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 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, Cambridge, MA 02110-1301, USA. */ #ifndef SVGDOCUMENTLOADEDIMPL_H #define SVGDOCUMENTLOADEDIMPL_H // Qt // KDE // Local #include namespace Gwenview { struct SvgDocumentLoadedImplPrivate; class SvgDocumentLoadedImpl : public AbstractDocumentImpl { Q_OBJECT public: SvgDocumentLoadedImpl(Document*, const QByteArray&); - ~SvgDocumentLoadedImpl() Q_DECL_OVERRIDE; + ~SvgDocumentLoadedImpl() override; - void init() Q_DECL_OVERRIDE; + void init() override; - Document::LoadingState loadingState() const Q_DECL_OVERRIDE; + Document::LoadingState loadingState() const override; virtual void setImage(const QImage&); - QByteArray rawData() const Q_DECL_OVERRIDE; + QByteArray rawData() const override; - QSvgRenderer* svgRenderer() const Q_DECL_OVERRIDE; + QSvgRenderer* svgRenderer() const override; private: SvgDocumentLoadedImplPrivate* const d; }; } // namespace #endif /* SVGDOCUMENTLOADEDIMPL_H */ diff --git a/lib/document/videodocumentloadedimpl.h b/lib/document/videodocumentloadedimpl.h index ddf23484..35538fd0 100644 --- a/lib/document/videodocumentloadedimpl.h +++ b/lib/document/videodocumentloadedimpl.h @@ -1,54 +1,54 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2009 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, Cambridge, MA 02110-1301, USA. */ #ifndef VIDEODOCUMENTLOADEDIMPL_H #define VIDEODOCUMENTLOADEDIMPL_H // Qt // KDE // Local #include namespace Gwenview { struct VideoDocumentLoadedImplPrivate; class VideoDocumentLoadedImpl : public AbstractDocumentImpl { Q_OBJECT public: VideoDocumentLoadedImpl(Document*); - ~VideoDocumentLoadedImpl() Q_DECL_OVERRIDE; + ~VideoDocumentLoadedImpl() override; - void init() Q_DECL_OVERRIDE; + void init() override; - Document::LoadingState loadingState() const Q_DECL_OVERRIDE; + Document::LoadingState loadingState() const override; virtual void setImage(const QImage&); private: VideoDocumentLoadedImplPrivate* const d; }; } // namespace #endif /* VIDEODOCUMENTLOADEDIMPL_H */ diff --git a/lib/documentonlyproxymodel.h b/lib/documentonlyproxymodel.h index 7f6590b0..e230cace 100644 --- a/lib/documentonlyproxymodel.h +++ b/lib/documentonlyproxymodel.h @@ -1,56 +1,56 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2013 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, Cambridge, MA 02110-1301, USA. */ #ifndef DOCUMENTONLYPROXYMODEL_H #define DOCUMENTONLYPROXYMODEL_H // Local #include // KDE // Qt #include namespace Gwenview { struct DocumentOnlyProxyModelPrivate; /** * A proxy model which lists items which are neither dirs nor archives. * Only works with models which expose a KDirModel::FileItemRole. */ class GWENVIEWLIB_EXPORT DocumentOnlyProxyModel : public QSortFilterProxyModel { Q_OBJECT public: explicit DocumentOnlyProxyModel(QObject* parent = nullptr); - ~DocumentOnlyProxyModel() Q_DECL_OVERRIDE; + ~DocumentOnlyProxyModel() override; protected: - bool filterAcceptsRow(int row, const QModelIndex& parent) const Q_DECL_OVERRIDE; + bool filterAcceptsRow(int row, const QModelIndex& parent) const override; private: DocumentOnlyProxyModelPrivate* const d; }; } // namespace #endif /* DOCUMENTONLYPROXYMODEL_H */ diff --git a/lib/documentview/abstractdocumentviewadapter.h b/lib/documentview/abstractdocumentviewadapter.h index c8d7fc22..672dffcd 100644 --- a/lib/documentview/abstractdocumentviewadapter.h +++ b/lib/documentview/abstractdocumentviewadapter.h @@ -1,203 +1,203 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 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, Cambridge, MA 02110-1301, USA. */ #ifndef ABSTRACTDOCUMENTVIEWADAPTER_H #define ABSTRACTDOCUMENTVIEWADAPTER_H #include // Qt #include #include // KDE // Local #include class QCursor; class QGraphicsWidget; class QRectF; namespace Gwenview { class AbstractImageView; class RasterImageView; /** * Classes inherit from this class so that they can be used inside the * DocumentPanel. */ class GWENVIEWLIB_EXPORT AbstractDocumentViewAdapter : public QObject { Q_OBJECT public: AbstractDocumentViewAdapter(); virtual ~AbstractDocumentViewAdapter(); QGraphicsWidget* widget() const { return mWidget; } virtual MimeTypeUtils::Kind kind() const = 0; virtual AbstractImageView* imageView() const { return nullptr; } virtual RasterImageView* rasterImageView() const { return nullptr; } virtual QCursor cursor() const; virtual void setCursor(const QCursor&); /** * @defgroup zooming functions * @{ */ virtual bool canZoom() const { return false; } // Implementation must emit zoomToFitChanged() virtual void setZoomToFit(bool) {} virtual bool zoomToFit() const { return false; } // Implementation must emit zoomToFillChanged() virtual void setZoomToFill(bool /*on*/, const QPointF& /*center*/ = QPointF(-1, -1)) {} virtual bool zoomToFill() const { return false; } virtual qreal zoom() const { return 0; } virtual void setZoom(qreal /*zoom*/, const QPointF& /*center*/ = QPointF(-1, -1)) {} virtual qreal computeZoomToFit() const { return 1.; } virtual qreal computeZoomToFill() const { return 1.; } /** @} */ virtual Document::Ptr document() const = 0; virtual void setDocument(Document::Ptr) = 0; virtual void loadConfig() {} virtual QPointF scrollPos() const { return QPointF(0, 0); } virtual void setScrollPos(const QPointF& /*pos*/) {} /** * Rectangle within the item which is actually used to show the document. * In item coordinates. */ virtual QRectF visibleDocumentRect() const; protected: void setWidget(QGraphicsWidget* widget) { mWidget = widget; } Q_SIGNALS: /** * @addgroup zooming functions * @{ */ void zoomChanged(qreal); void zoomToFitChanged(bool); void zoomToFillChanged(bool); void zoomInRequested(const QPointF&); void zoomOutRequested(const QPointF&); /** @} */ void scrollPosChanged(); /** * Emitted when the adapter is done showing the document for the first time */ void completed(); void previousImageRequested(); void nextImageRequested(); void toggleFullScreenRequested(); private: QGraphicsWidget* mWidget; }; /** * An empty adapter, used when no document is displayed */ class EmptyAdapter : public AbstractDocumentViewAdapter { Q_OBJECT public: EmptyAdapter(); - MimeTypeUtils::Kind kind() const Q_DECL_OVERRIDE + MimeTypeUtils::Kind kind() const override { return MimeTypeUtils::KIND_UNKNOWN; } - Document::Ptr document() const Q_DECL_OVERRIDE + Document::Ptr document() const override { return Document::Ptr(); } - void setDocument(Document::Ptr) Q_DECL_OVERRIDE + void setDocument(Document::Ptr) override {} }; } // namespace #endif /* ABSTRACTDOCUMENTVIEWADAPTER_H */ diff --git a/lib/documentview/abstractimageview.h b/lib/documentview/abstractimageview.h index 5cc90ae3..f5a211e9 100644 --- a/lib/documentview/abstractimageview.h +++ b/lib/documentview/abstractimageview.h @@ -1,160 +1,160 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2011 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, Cambridge, MA 02110-1301, USA. */ #ifndef ABSTRACTIMAGEVIEW_H #define ABSTRACTIMAGEVIEW_H // Local #include // KDE // Qt #include namespace Gwenview { struct AbstractImageViewPrivate; /** * */ class AbstractImageView : public QGraphicsWidget { Q_OBJECT public: enum UpdateType { UpdateIfNecessary, ForceUpdate }; enum AlphaBackgroundMode { AlphaBackgroundNone, AlphaBackgroundCheckBoard, AlphaBackgroundSolid }; AbstractImageView(QGraphicsItem* parent); - ~AbstractImageView() Q_DECL_OVERRIDE; + ~AbstractImageView() override; qreal zoom() const; virtual void setZoom(qreal zoom, const QPointF& center = QPointF(-1, -1), UpdateType updateType = UpdateIfNecessary); bool zoomToFit() const; bool zoomToFill() const; virtual void setZoomToFit(bool value); virtual void setZoomToFill(bool value, const QPointF& center = QPointF(-1, -1)); virtual void setDocument(Document::Ptr doc); Document::Ptr document() const; qreal computeZoomToFit() const; qreal computeZoomToFill() const; QSizeF documentSize() const; QSizeF visibleImageSize() const; /** * If the image is smaller than the view, imageOffset is the distance from * the topleft corner of the view to the topleft corner of the image. * Neither x nor y can be negative. */ QPointF imageOffset() const; /** * The scroll position, in zoomed image coordinates. * x and y are always between 0 and (docsize * zoom - viewsize) */ QPointF scrollPos() const; void setScrollPos(const QPointF& pos); QPointF mapToView(const QPointF& imagePos) const; QPoint mapToView(const QPoint& imagePos) const; QRectF mapToView(const QRectF& imageRect) const; QRect mapToView(const QRect& imageRect) const; QPointF mapToImage(const QPointF& viewPos) const; QPoint mapToImage(const QPoint& viewPos) const; QRectF mapToImage(const QRectF& viewRect) const; QRect mapToImage(const QRect& viewRect) const; void setEnlargeSmallerImages(bool value); void applyPendingScrollPos(); void resetDragCursor(); public Q_SLOTS: void updateCursor(); Q_SIGNALS: void zoomToFitChanged(bool); void zoomToFillChanged(bool); void zoomChanged(qreal); void zoomInRequested(const QPointF&); void zoomOutRequested(const QPointF&); void scrollPosChanged(); void completed(); void previousImageRequested(); void nextImageRequested(); void toggleFullScreenRequested(); protected: virtual void setAlphaBackgroundMode(AlphaBackgroundMode mode) = 0; virtual void setAlphaBackgroundColor(const QColor& color) = 0; const QPixmap& alphaBackgroundTexture() const; virtual void loadFromDocument() = 0; virtual void onZoomChanged() = 0; /** * Called when the offset changes. * Note: to avoid multiple adjustments, this is not called if zoom changes! */ virtual void onImageOffsetChanged() = 0; /** * Called when the scrollPos changes. * Note: to avoid multiple adjustments, this is not called if zoom changes! */ virtual void onScrollPosChanged(const QPointF& oldPos) = 0; - void resizeEvent(QGraphicsSceneResizeEvent* event) Q_DECL_OVERRIDE; - void focusInEvent(QFocusEvent* event) Q_DECL_OVERRIDE; + void resizeEvent(QGraphicsSceneResizeEvent* event) override; + void focusInEvent(QFocusEvent* event) override; - void keyPressEvent(QKeyEvent* event) Q_DECL_OVERRIDE; - void keyReleaseEvent(QKeyEvent* event) Q_DECL_OVERRIDE; - void mousePressEvent(QGraphicsSceneMouseEvent* event) Q_DECL_OVERRIDE; - void mouseMoveEvent(QGraphicsSceneMouseEvent* event) Q_DECL_OVERRIDE; - void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) Q_DECL_OVERRIDE; - void mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) Q_DECL_OVERRIDE; + void keyPressEvent(QKeyEvent* event) override; + void keyReleaseEvent(QKeyEvent* event) override; + void mousePressEvent(QGraphicsSceneMouseEvent* event) override; + void mouseMoveEvent(QGraphicsSceneMouseEvent* event) override; + void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) override; + void mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) override; private: friend struct AbstractImageViewPrivate; AbstractImageViewPrivate* const d; }; } // namespace #endif /* ABSTRACTIMAGEVIEW_H */ diff --git a/lib/documentview/birdeyeview.h b/lib/documentview/birdeyeview.h index 9ef39e01..feaf2f6d 100644 --- a/lib/documentview/birdeyeview.h +++ b/lib/documentview/birdeyeview.h @@ -1,80 +1,80 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2011 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, Cambridge, MA 02110-1301, USA. */ #ifndef BIRDEYEVIEW_H #define BIRDEYEVIEW_H // Local // KDE // Qt #include namespace Gwenview { class DocumentView; struct BirdEyeViewPrivate; /** * Shows a bird-eye view of the current document. Makes it possible to scroll * through the document. */ class BirdEyeView : public QGraphicsWidget { Q_OBJECT public: explicit BirdEyeView(DocumentView* docView); - ~BirdEyeView() Q_DECL_OVERRIDE; + ~BirdEyeView() override; - void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = nullptr) Q_DECL_OVERRIDE; + void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = nullptr) override; // Called by DocumentView when it detects mouse movements // We cannot use a sceneEventFilter because QGraphicsSceneHoverEvent are not // sent to parent items (unlike QHoverEvent). Therefore DocumentView has to // do the work of event-filtering the actual document widget. void onMouseMoved(); public Q_SLOTS: void slotZoomOrSizeChanged(); protected: - void mousePressEvent(QGraphicsSceneMouseEvent* event) Q_DECL_OVERRIDE; - void mouseMoveEvent(QGraphicsSceneMouseEvent* event) Q_DECL_OVERRIDE; - void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) Q_DECL_OVERRIDE; - void hoverEnterEvent(QGraphicsSceneHoverEvent* event) Q_DECL_OVERRIDE; - void hoverMoveEvent(QGraphicsSceneHoverEvent* event) Q_DECL_OVERRIDE; - void hoverLeaveEvent(QGraphicsSceneHoverEvent* event) Q_DECL_OVERRIDE; + void mousePressEvent(QGraphicsSceneMouseEvent* event) override; + void mouseMoveEvent(QGraphicsSceneMouseEvent* event) override; + void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) override; + void hoverEnterEvent(QGraphicsSceneHoverEvent* event) override; + void hoverMoveEvent(QGraphicsSceneHoverEvent* event) override; + void hoverLeaveEvent(QGraphicsSceneHoverEvent* event) override; private Q_SLOTS: void slotAutoHideTimeout(); void slotPositionChanged(); void slotIsAnimatedChanged(); private: BirdEyeViewPrivate* const d; void adjustVisibleRect(); void adjustGeometry(); }; } // namespace #endif /* BIRDEYEVIEW_H */ diff --git a/lib/documentview/documentview.cpp b/lib/documentview/documentview.cpp index fa9d3f5b..e0dd5d2f 100644 --- a/lib/documentview/documentview.cpp +++ b/lib/documentview/documentview.cpp @@ -1,1015 +1,1015 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 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, Cambridge, MA 02110-1301, USA. */ // Self #include "documentview.h" // C++ Standard library #include // Qt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // KDE #include #include #include // Local #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Gwenview { #undef ENABLE_LOG #undef LOG //#define ENABLE_LOG #ifdef ENABLE_LOG #define LOG(x) //qDebug() << x #else #define LOG(x) ; #endif static const qreal REAL_DELTA = 0.001; static const qreal MAXIMUM_ZOOM_VALUE = qreal(DocumentView::MaximumZoom); static const auto MINSTEP = sqrt(0.5); static const auto MAXSTEP = sqrt(2.0); static const int COMPARE_MARGIN = 4; const int DocumentView::MaximumZoom = 16; const int DocumentView::AnimDuration = 250; struct DocumentViewPrivate { DocumentView* q; int mSortKey; // Used to sort views when displayed in compare mode HudWidget* mHud; BirdEyeView* mBirdEyeView; QPointer mMoveAnimation; QPointer mFadeAnimation; QGraphicsOpacityEffect* mOpacityEffect; LoadingIndicator* mLoadingIndicator; QScopedPointer mAdapter; QList mZoomSnapValues; Document::Ptr mDocument; DocumentView::Setup mSetup; bool mCurrent; bool mCompareMode; int controlWheelAccumulatedDelta; QPointF mDragStartPosition; QPointer mDragThumbnailProvider; QPointer mDrag; void setCurrentAdapter(AbstractDocumentViewAdapter* adapter) { Q_ASSERT(adapter); mAdapter.reset(adapter); adapter->widget()->setParentItem(q); resizeAdapterWidget(); if (adapter->canZoom()) { QObject::connect(adapter, SIGNAL(zoomChanged(qreal)), q, SLOT(slotZoomChanged(qreal))); QObject::connect(adapter, SIGNAL(zoomInRequested(QPointF)), q, SLOT(zoomIn(QPointF))); QObject::connect(adapter, SIGNAL(zoomOutRequested(QPointF)), q, SLOT(zoomOut(QPointF))); QObject::connect(adapter, SIGNAL(zoomToFitChanged(bool)), q, SIGNAL(zoomToFitChanged(bool))); QObject::connect(adapter, SIGNAL(zoomToFillChanged(bool)), q, SIGNAL(zoomToFillChanged(bool))); } QObject::connect(adapter, SIGNAL(scrollPosChanged()), q, SIGNAL(positionChanged())); QObject::connect(adapter, SIGNAL(previousImageRequested()), q, SIGNAL(previousImageRequested())); QObject::connect(adapter, SIGNAL(nextImageRequested()), q, SIGNAL(nextImageRequested())); QObject::connect(adapter, SIGNAL(toggleFullScreenRequested()), q, SIGNAL(toggleFullScreenRequested())); QObject::connect(adapter, SIGNAL(completed()), q, SLOT(slotCompleted())); adapter->loadConfig(); adapter->widget()->installSceneEventFilter(q); if (mCurrent) { adapter->widget()->setFocus(); } if (mSetup.valid && adapter->canZoom()) { adapter->setZoomToFit(mSetup.zoomToFit); adapter->setZoomToFill(mSetup.zoomToFill); if (!mSetup.zoomToFit && !mSetup.zoomToFill) { adapter->setZoom(mSetup.zoom); adapter->setScrollPos(mSetup.position); } } q->adapterChanged(); q->positionChanged(); if (adapter->canZoom()) { if (adapter->zoomToFit()) { q->zoomToFitChanged(true); } else if (adapter->zoomToFill()) { q->zoomToFillChanged(true); } else { q->zoomChanged(adapter->zoom()); } } if (adapter->rasterImageView()) { QObject::connect(adapter->rasterImageView(), SIGNAL(currentToolChanged(AbstractRasterImageViewTool*)), q, SIGNAL(currentToolChanged(AbstractRasterImageViewTool*))); } } void setupLoadingIndicator() { mLoadingIndicator = new LoadingIndicator(q); GraphicsWidgetFloater* floater = new GraphicsWidgetFloater(q); floater->setChildWidget(mLoadingIndicator); } - HudButton* createHudButton(const QString& text, const char* iconName, bool showText) + HudButton* createHudButton(const QString& text, const QString &iconName, bool showText) { HudButton* button = new HudButton; if (showText) { button->setText(text); } else { button->setToolTip(text); } button->setIcon(QIcon::fromTheme(iconName)); return button; } void setupHud() { - HudButton* trashButton = createHudButton(i18nc("@info:tooltip", "Trash"), "user-trash", false); - HudButton* deselectButton = createHudButton(i18nc("@action:button", "Deselect"), "list-remove", true); + HudButton* trashButton = createHudButton(i18nc("@info:tooltip", "Trash"), QStringLiteral("user-trash"), false); + HudButton* deselectButton = createHudButton(i18nc("@action:button", "Deselect"), QStringLiteral("list-remove"), true); QGraphicsWidget* content = new QGraphicsWidget; QGraphicsLinearLayout* layout = new QGraphicsLinearLayout(content); layout->addItem(trashButton); layout->addItem(deselectButton); mHud = new HudWidget(q); mHud->init(content, HudWidget::OptionNone); GraphicsWidgetFloater* floater = new GraphicsWidgetFloater(q); floater->setChildWidget(mHud); floater->setAlignment(Qt::AlignBottom | Qt::AlignHCenter); QObject::connect(trashButton, SIGNAL(clicked()), q, SLOT(emitHudTrashClicked())); QObject::connect(deselectButton, SIGNAL(clicked()), q, SLOT(emitHudDeselectClicked())); mHud->hide(); } void setupBirdEyeView() { if (mBirdEyeView) { delete mBirdEyeView; } mBirdEyeView = new BirdEyeView(q); mBirdEyeView->setZValue(1); } void updateCaption() { if (!mCurrent) { return; } QString caption; Document::Ptr doc = mAdapter->document(); if (!doc) { emit q->captionUpdateRequested(caption); return; } caption = doc->url().fileName(); QSize size = doc->size(); if (size.isValid()) { caption += - QString(" - %1x%2") + QStringLiteral(" - %1x%2") .arg(size.width()) .arg(size.height()); if (mAdapter->canZoom()) { int intZoom = qRound(mAdapter->zoom() * 100); - caption += QString(" - %1%") + caption += QStringLiteral(" - %1%") .arg(intZoom); } } emit q->captionUpdateRequested(caption); } void uncheckZoomToFit() { if (mAdapter->zoomToFit()) { mAdapter->setZoomToFit(false); } } void uncheckZoomToFill() { if (mAdapter->zoomToFill()) { mAdapter->setZoomToFill(false); } } void setZoom(qreal zoom, const QPointF& center = QPointF(-1, -1)) { uncheckZoomToFit(); uncheckZoomToFill(); zoom = qBound(q->minimumZoom(), zoom, MAXIMUM_ZOOM_VALUE); mAdapter->setZoom(zoom, center); } void updateZoomSnapValues() { qreal min = q->minimumZoom(); mZoomSnapValues.clear(); for (qreal zoom = MINSTEP; zoom > min; zoom *= MINSTEP) { mZoomSnapValues << zoom; } mZoomSnapValues << min; std::reverse(mZoomSnapValues.begin(), mZoomSnapValues.end()); for (qreal zoom = 1; zoom < MAXIMUM_ZOOM_VALUE; zoom *= MAXSTEP) { mZoomSnapValues << zoom; } mZoomSnapValues << MAXIMUM_ZOOM_VALUE; q->minimumZoomChanged(min); } void showLoadingIndicator() { if (!mLoadingIndicator) { setupLoadingIndicator(); } mLoadingIndicator->show(); mLoadingIndicator->setZValue(1); } void hideLoadingIndicator() { if (!mLoadingIndicator) { return; } mLoadingIndicator->hide(); } void resizeAdapterWidget() { QRectF rect = QRectF(QPointF(0, 0), q->boundingRect().size()); if (mCompareMode) { rect.adjust(COMPARE_MARGIN, COMPARE_MARGIN, -COMPARE_MARGIN, -COMPARE_MARGIN); } mAdapter->widget()->setGeometry(rect); } void fadeTo(qreal value) { if (mFadeAnimation.data()) { qreal endValue = mFadeAnimation.data()->endValue().toReal(); if (qFuzzyCompare(value, endValue)) { // Same end value, don't change the actual animation return; } } // Create a new fade animation QPropertyAnimation* anim = new QPropertyAnimation(mOpacityEffect, "opacity"); anim->setStartValue(mOpacityEffect->opacity()); anim->setEndValue(value); if (qFuzzyCompare(value, 1)) { QObject::connect(anim, SIGNAL(finished()), q, SLOT(slotFadeInFinished())); } QObject::connect(anim, SIGNAL(finished()), q, SIGNAL(isAnimatedChanged())); anim->setDuration(DocumentView::AnimDuration); mFadeAnimation = anim; q->isAnimatedChanged(); anim->start(QAbstractAnimation::DeleteWhenStopped); } bool canPan() const { if (!q->canZoom()) { return false; } const QSize zoomedImageSize = mDocument->size() * q->zoom(); const QSize viewPortSize = q->boundingRect().size().toSize(); const bool imageWiderThanViewport = zoomedImageSize.width() > viewPortSize.width(); const bool imageTallerThanViewport = zoomedImageSize.height() > viewPortSize.height(); return (imageWiderThanViewport || imageTallerThanViewport); } void setDragPixmap(const QPixmap& pix) { if (mDrag) { DragPixmapGenerator::DragPixmap dragPixmap = DragPixmapGenerator::generate({pix}, 1); mDrag->setPixmap(dragPixmap.pix); mDrag->setHotSpot(dragPixmap.hotSpot); } } void executeDrag() { if (mDrag) { if (mAdapter->imageView()) { mAdapter->imageView()->resetDragCursor(); } mDrag->exec(Qt::MoveAction | Qt::CopyAction | Qt::LinkAction, Qt::CopyAction); } } void initDragThumbnailProvider() { mDragThumbnailProvider = new ThumbnailProvider(); QObject::connect(mDragThumbnailProvider, &ThumbnailProvider::thumbnailLoaded, q, &DocumentView::dragThumbnailLoaded); QObject::connect(mDragThumbnailProvider, &ThumbnailProvider::thumbnailLoadingFailed, q, &DocumentView::dragThumbnailLoadingFailed); } void startDragIfSensible() { if (q->document()->loadingState() == Document::LoadingFailed) { return; } if (q->currentTool()) { return; } if (mDrag) { mDrag->deleteLater(); } mDrag = new QDrag(q); const auto itemList = KFileItemList({q->document()->url()}); mDrag->setMimeData(MimeTypeUtils::selectionMimeData(itemList)); if (q->document()->isModified()) { setDragPixmap(QPixmap::fromImage(q->document()->image())); executeDrag(); } else { // Drag is triggered on success or failure of thumbnail generation if (mDragThumbnailProvider.isNull()) { initDragThumbnailProvider(); } mDragThumbnailProvider->appendItems(itemList); } } QPointF cursorPosition() { const QGraphicsScene* sc = q->scene(); if (sc) { const auto views = sc->views(); for (const QGraphicsView* view : views) { if (view->underMouse()) { return q->mapFromScene(view->mapFromGlobal(QCursor::pos())); } } } return QPointF(-1, -1); } }; DocumentView::DocumentView(QGraphicsScene* scene) : d(new DocumentViewPrivate) { setFlag(ItemIsFocusable); setFlag(ItemIsSelectable); setFlag(ItemClipsChildrenToShape); d->q = this; d->mLoadingIndicator = nullptr; d->mBirdEyeView = nullptr; d->mCurrent = false; d->mCompareMode = false; d->controlWheelAccumulatedDelta = 0; d->mDragStartPosition = QPointF(0, 0); d->mDrag = nullptr; // We use an opacity effect instead of using the opacity property directly, because the latter operates at // the painter level, which means if you draw multiple layers in paint(), all layers get the specified // opacity, resulting in all layers being visible when 0 < opacity < 1. // QGraphicsEffects on the other hand, operate after all painting is done, therefore 'flattening' all layers. // This is important for fade effects, where we don't want any background layers visible during the fade. d->mOpacityEffect = new QGraphicsOpacityEffect(this); d->mOpacityEffect->setOpacity(0); setGraphicsEffect(d->mOpacityEffect); scene->addItem(this); d->setupHud(); d->setCurrentAdapter(new EmptyAdapter); setAcceptDrops(true); } DocumentView::~DocumentView() { delete d->mDragThumbnailProvider; delete d->mDrag; delete d; } void DocumentView::createAdapterForDocument() { const MimeTypeUtils::Kind documentKind = d->mDocument->kind(); if (d->mAdapter && documentKind == d->mAdapter->kind() && documentKind != MimeTypeUtils::KIND_UNKNOWN) { // Do not reuse for KIND_UNKNOWN: we may need to change the message LOG("Reusing current adapter"); return; } AbstractDocumentViewAdapter* adapter = nullptr; switch (documentKind) { case MimeTypeUtils::KIND_RASTER_IMAGE: adapter = new RasterImageViewAdapter; break; case MimeTypeUtils::KIND_SVG_IMAGE: adapter = new SvgViewAdapter; break; case MimeTypeUtils::KIND_VIDEO: adapter = new VideoViewAdapter; connect(adapter, SIGNAL(videoFinished()), SIGNAL(videoFinished())); break; case MimeTypeUtils::KIND_UNKNOWN: adapter = new MessageViewAdapter; static_cast(adapter)->setErrorMessage(i18n("Gwenview does not know how to display this kind of document")); break; default: qWarning() << "should not be called for documentKind=" << documentKind; adapter = new MessageViewAdapter; break; } d->setCurrentAdapter(adapter); } void DocumentView::openUrl(const QUrl &url, const DocumentView::Setup& setup) { if (d->mDocument) { if (url == d->mDocument->url()) { return; } disconnect(d->mDocument.data(), nullptr, this, nullptr); } d->mSetup = setup; d->mDocument = DocumentFactory::instance()->load(url); connect(d->mDocument.data(), SIGNAL(busyChanged(QUrl,bool)), SLOT(slotBusyChanged(QUrl,bool))); connect(d->mDocument.data(), &Document::modified, this, [this]() { d->updateZoomSnapValues(); }); if (d->mDocument->loadingState() < Document::KindDetermined) { MessageViewAdapter* messageViewAdapter = qobject_cast(d->mAdapter.data()); if (messageViewAdapter) { messageViewAdapter->setInfoMessage(QString()); } d->showLoadingIndicator(); connect(d->mDocument.data(), SIGNAL(kindDetermined(QUrl)), SLOT(finishOpenUrl())); } else { QMetaObject::invokeMethod(this, "finishOpenUrl", Qt::QueuedConnection); } d->setupBirdEyeView(); } void DocumentView::finishOpenUrl() { disconnect(d->mDocument.data(), SIGNAL(kindDetermined(QUrl)), this, SLOT(finishOpenUrl())); GV_RETURN_IF_FAIL(d->mDocument->loadingState() >= Document::KindDetermined); if (d->mDocument->loadingState() == Document::LoadingFailed) { slotLoadingFailed(); return; } createAdapterForDocument(); connect(d->mDocument.data(), SIGNAL(loadingFailed(QUrl)), SLOT(slotLoadingFailed())); d->mAdapter->setDocument(d->mDocument); d->updateCaption(); } void DocumentView::loadAdapterConfig() { d->mAdapter->loadConfig(); } RasterImageView* DocumentView::imageView() const { return d->mAdapter->rasterImageView(); } void DocumentView::slotCompleted() { d->hideLoadingIndicator(); d->updateCaption(); d->updateZoomSnapValues(); if (!d->mAdapter->zoomToFit() || !d->mAdapter->zoomToFill()) { qreal min = minimumZoom(); if (d->mAdapter->zoom() < min) { d->mAdapter->setZoom(min); } } emit completed(); } DocumentView::Setup DocumentView::setup() const { Setup setup; if (d->mAdapter->canZoom()) { setup.valid = true; setup.zoomToFit = zoomToFit(); setup.zoomToFill = zoomToFill(); if (!setup.zoomToFit && !setup.zoomToFill) { setup.zoom = zoom(); setup.position = position(); } } return setup; } void DocumentView::slotLoadingFailed() { d->hideLoadingIndicator(); MessageViewAdapter* adapter = new MessageViewAdapter; adapter->setDocument(d->mDocument); QString message = xi18n("Loading %1 failed", d->mDocument->url().fileName()); adapter->setErrorMessage(message, d->mDocument->errorString()); d->setCurrentAdapter(adapter); emit completed(); } bool DocumentView::canZoom() const { return d->mAdapter->canZoom(); } void DocumentView::setZoomToFit(bool on) { if (on == d->mAdapter->zoomToFit()) { return; } d->mAdapter->setZoomToFit(on); } void DocumentView::toggleZoomToFit() { const bool zoomToFitOn = d->mAdapter->zoomToFit(); d->mAdapter->setZoomToFit(!zoomToFitOn); if (zoomToFitOn) { d->setZoom(1., d->cursorPosition()); } } void DocumentView::setZoomToFill(bool on) { if (on == d->mAdapter->zoomToFill()) { return; } d->mAdapter->setZoomToFill(on, d->cursorPosition()); } void DocumentView::toggleZoomToFill() { const bool zoomToFillOn = d->mAdapter->zoomToFill(); d->mAdapter->setZoomToFill(!zoomToFillOn, d->cursorPosition()); if (zoomToFillOn) { d->setZoom(1., d->cursorPosition()); } } bool DocumentView::zoomToFit() const { return d->mAdapter->zoomToFit(); } bool DocumentView::zoomToFill() const { return d->mAdapter->zoomToFill(); } void DocumentView::zoomActualSize() { d->uncheckZoomToFit(); d->uncheckZoomToFill(); d->mAdapter->setZoom(1., d->cursorPosition()); } void DocumentView::zoomIn(QPointF center) { if (center == QPointF(-1, -1)) { center = d->cursorPosition(); } qreal currentZoom = d->mAdapter->zoom(); Q_FOREACH(qreal zoom, d->mZoomSnapValues) { if (zoom > currentZoom + REAL_DELTA) { d->setZoom(zoom, center); return; } } } void DocumentView::zoomOut(QPointF center) { if (center == QPointF(-1, -1)) { center = d->cursorPosition(); } qreal currentZoom = d->mAdapter->zoom(); QListIterator it(d->mZoomSnapValues); it.toBack(); while (it.hasPrevious()) { qreal zoom = it.previous(); if (zoom < currentZoom - REAL_DELTA) { d->setZoom(zoom, center); return; } } } void DocumentView::slotZoomChanged(qreal zoom) { d->updateCaption(); zoomChanged(zoom); } void DocumentView::setZoom(qreal zoom) { d->setZoom(zoom); } qreal DocumentView::zoom() const { return d->mAdapter->zoom(); } void DocumentView::resizeEvent(QGraphicsSceneResizeEvent *event) { d->resizeAdapterWidget(); d->updateZoomSnapValues(); QGraphicsWidget::resizeEvent(event); } void DocumentView::mousePressEvent(QGraphicsSceneMouseEvent* event) { QGraphicsWidget::mousePressEvent(event); if (d->mAdapter->canZoom() && event->button() == Qt::MiddleButton) { if (event->modifiers() == Qt::NoModifier) { toggleZoomToFit(); } else if (event->modifiers() == Qt::SHIFT) { toggleZoomToFill(); } } } void DocumentView::wheelEvent(QGraphicsSceneWheelEvent* event) { if (d->mAdapter->canZoom() && event->modifiers() & Qt::ControlModifier) { d->controlWheelAccumulatedDelta += event->delta(); // Ctrl + wheel => zoom in or out if (d->controlWheelAccumulatedDelta >= QWheelEvent::DefaultDeltasPerStep) { zoomIn(event->pos()); d->controlWheelAccumulatedDelta = 0; } else if (d->controlWheelAccumulatedDelta <= -QWheelEvent::DefaultDeltasPerStep) { zoomOut(event->pos()); d->controlWheelAccumulatedDelta = 0; } return; } if (GwenviewConfig::mouseWheelBehavior() == MouseWheelBehavior::Browse && event->modifiers() == Qt::NoModifier) { d->controlWheelAccumulatedDelta += event->delta(); // Browse with mouse wheel if (d->controlWheelAccumulatedDelta >= QWheelEvent::DefaultDeltasPerStep) { previousImageRequested(); d->controlWheelAccumulatedDelta = 0; } else if (d->controlWheelAccumulatedDelta <= -QWheelEvent::DefaultDeltasPerStep) { nextImageRequested(); d->controlWheelAccumulatedDelta = 0; } return; } // Scroll qreal dx = 0; // 16 = pixels for one line // 120: see QWheelEvent::delta() doc qreal dy = -qApp->wheelScrollLines() * 16 * event->delta() / 120; if (event->orientation() == Qt::Horizontal) { qSwap(dx, dy); } d->mAdapter->setScrollPos(d->mAdapter->scrollPos() + QPointF(dx, dy)); } void DocumentView::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) { // Filter out context menu if Ctrl is down to avoid showing it when // zooming out with Ctrl + Right button if (event->modifiers() != Qt::ControlModifier) { contextMenuRequested(); } } void DocumentView::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/) { // Fill background manually, because setAutoFillBackground(true) fill with QPalette::Window, // but our palettes use QPalette::Base for the background color/texture painter->fillRect(rect(), palette().base()); // Selection indicator/highlight if (d->mCompareMode && d->mCurrent) { painter->save(); painter->setBrush(Qt::NoBrush); painter->setPen(QPen(palette().highlight().color(), 2)); painter->setRenderHint(QPainter::Antialiasing); const QRectF visibleRectF = mapRectFromItem(d->mAdapter->widget(), d->mAdapter->visibleDocumentRect()); // Round the point and size independently. This is different than calling toRect(), // and is necessary to keep consistent rects, otherwise the selection rect can be // drawn 1 pixel too big or small. const QRect visibleRect = QRect(visibleRectF.topLeft().toPoint(), visibleRectF.size().toSize()); const QRect selectionRect = visibleRect.adjusted(-1, -1, 1, 1); painter->drawRoundedRect(selectionRect, 3, 3); painter->restore(); } } void DocumentView::slotBusyChanged(const QUrl&, bool busy) { if (busy) { d->showLoadingIndicator(); } else { d->hideLoadingIndicator(); } } qreal DocumentView::minimumZoom() const { // There is no point zooming out less than zoomToFit, but make sure it does // not get too small either return qBound(qreal(0.001), d->mAdapter->computeZoomToFit(), qreal(1.)); } void DocumentView::setCompareMode(bool compare) { d->mCompareMode = compare; if (compare) { d->mHud->show(); d->mHud->setZValue(1); } else { d->mHud->hide(); } } void DocumentView::setCurrent(bool value) { d->mCurrent = value; if (value) { d->mAdapter->widget()->setFocus(); d->updateCaption(); } update(); } bool DocumentView::isCurrent() const { return d->mCurrent; } QPoint DocumentView::position() const { return d->mAdapter->scrollPos().toPoint(); } void DocumentView::setPosition(const QPoint& pos) { d->mAdapter->setScrollPos(pos); } Document::Ptr DocumentView::document() const { return d->mDocument; } QUrl DocumentView::url() const { Document::Ptr doc = d->mDocument; return doc ? doc->url() : QUrl(); } void DocumentView::emitHudDeselectClicked() { hudDeselectClicked(this); } void DocumentView::emitHudTrashClicked() { hudTrashClicked(this); } void DocumentView::emitFocused() { focused(this); } void DocumentView::setGeometry(const QRectF& rect) { QGraphicsWidget::setGeometry(rect); if (d->mBirdEyeView) { d->mBirdEyeView->slotZoomOrSizeChanged(); } } void DocumentView::moveTo(const QRect& rect) { if (d->mMoveAnimation) { d->mMoveAnimation.data()->setEndValue(rect); } else { setGeometry(rect); } } void DocumentView::moveToAnimated(const QRect& rect) { QPropertyAnimation* anim = new QPropertyAnimation(this, "geometry"); anim->setStartValue(geometry()); anim->setEndValue(rect); anim->setDuration(DocumentView::AnimDuration); connect(anim, SIGNAL(finished()), SIGNAL(isAnimatedChanged())); d->mMoveAnimation = anim; isAnimatedChanged(); anim->start(QAbstractAnimation::DeleteWhenStopped); } QPropertyAnimation* DocumentView::fadeIn() { d->fadeTo(1); return d->mFadeAnimation.data(); } void DocumentView::fadeOut() { d->fadeTo(0); } void DocumentView::slotFadeInFinished() { fadeInFinished(this); } bool DocumentView::isAnimated() const { return d->mMoveAnimation || d->mFadeAnimation; } bool DocumentView::sceneEventFilter(QGraphicsItem*, QEvent* event) { if (event->type() == QEvent::GraphicsSceneMousePress) { const QGraphicsSceneMouseEvent* mouseEvent = static_cast(event); if (mouseEvent->button() == Qt::LeftButton) { d->mDragStartPosition = mouseEvent->pos(); } QMetaObject::invokeMethod(this, "emitFocused", Qt::QueuedConnection); } else if (event->type() == QEvent::GraphicsSceneHoverMove) { if (d->mBirdEyeView) { d->mBirdEyeView->onMouseMoved(); } } else if (event->type() == QEvent::GraphicsSceneMouseMove) { const QGraphicsSceneMouseEvent* mouseEvent = static_cast(event); const qreal dragDistance = (mouseEvent->pos() - d->mDragStartPosition).manhattanLength(); const qreal minDistanceToStartDrag = QGuiApplication::styleHints()->startDragDistance(); if (!d->canPan() && dragDistance >= minDistanceToStartDrag) { d->startDragIfSensible(); } } return false; } AbstractRasterImageViewTool* DocumentView::currentTool() const { return imageView() ? imageView()->currentTool() : nullptr; } int DocumentView::sortKey() const { return d->mSortKey; } void DocumentView::setSortKey(int sortKey) { d->mSortKey = sortKey; } void DocumentView::hideAndDeleteLater() { hide(); deleteLater(); } void DocumentView::setGraphicsEffectOpacity(qreal opacity) { d->mOpacityEffect->setOpacity(opacity); } void DocumentView::dragEnterEvent(QGraphicsSceneDragDropEvent* event) { QGraphicsWidget::dragEnterEvent(event); const auto urls = KUrlMimeData::urlsFromMimeData(event->mimeData()); bool acceptDrag = !urls.isEmpty(); if (urls.size() == 1 && urls.first() == url()) { // Do not allow dragging a single image onto itself acceptDrag = false; } event->setAccepted(acceptDrag); } void DocumentView::dropEvent(QGraphicsSceneDragDropEvent* event) { QGraphicsWidget::dropEvent(event); // Since we're capturing drops in View mode, we only support one url const QUrl url = event->mimeData()->urls().first(); if (UrlUtils::urlIsDirectory(url)) { emit openDirUrlRequested(url); } else { emit openUrlRequested(url); } } void DocumentView::dragThumbnailLoaded(const KFileItem& item, const QPixmap& pix) { d->setDragPixmap(pix); d->executeDrag(); d->mDragThumbnailProvider->removeItems(KFileItemList({item})); } void DocumentView::dragThumbnailLoadingFailed(const KFileItem& item) { d->executeDrag(); d->mDragThumbnailProvider->removeItems(KFileItemList({item})); } } // namespace diff --git a/lib/documentview/documentview.h b/lib/documentview/documentview.h index 684ad996..a2da3d8a 100644 --- a/lib/documentview/documentview.h +++ b/lib/documentview/documentview.h @@ -1,246 +1,246 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 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, Cambridge, MA 02110-1301, USA. */ #ifndef DOCUMENTVIEW_H #define DOCUMENTVIEW_H #include // Qt #include // KDE // Local #include class QPropertyAnimation; class QUrl; namespace Gwenview { class AbstractRasterImageViewTool; class RasterImageView; struct DocumentViewPrivate; /** * This widget can display various documents, using an instance of * AbstractDocumentViewAdapter */ class GWENVIEWLIB_EXPORT DocumentView : public QGraphicsWidget { Q_OBJECT Q_PROPERTY(qreal zoom READ zoom WRITE setZoom NOTIFY zoomChanged) Q_PROPERTY(bool zoomToFit READ zoomToFit WRITE setZoomToFit NOTIFY zoomToFitChanged) Q_PROPERTY(bool zoomToFill READ zoomToFill WRITE setZoomToFill NOTIFY zoomToFillChanged) Q_PROPERTY(QPoint position READ position WRITE setPosition NOTIFY positionChanged) public: static const int MaximumZoom; static const int AnimDuration; struct Setup { Setup() : valid(false) , zoomToFit(true) , zoomToFill(false) , zoom(0) {} bool valid:1; bool zoomToFit:1; bool zoomToFill:1; qreal zoom; QPointF position; }; enum AnimationMethod { NoAnimation, SoftwareAnimation, GLAnimation }; /** * Create a new view attached to scene. We need the scene to be able to * install scene event filters. */ explicit DocumentView(QGraphicsScene* scene); - ~DocumentView() Q_DECL_OVERRIDE; + ~DocumentView() override; Document::Ptr document() const; QUrl url() const; void openUrl(const QUrl&, const Setup&); Setup setup() const; /** * Tells the current adapter to load its config. Used when the user changed * the config while the view was visible. */ void loadAdapterConfig(); bool canZoom() const; qreal minimumZoom() const; qreal zoom() const; bool isCurrent() const; void setCurrent(bool); void setCompareMode(bool); bool zoomToFit() const; bool zoomToFill() const; QPoint position() const; /** * Returns the RasterImageView of the current adapter, if it has one */ RasterImageView* imageView() const; AbstractRasterImageViewTool* currentTool() const; void moveTo(const QRect&); void moveToAnimated(const QRect&); QPropertyAnimation* fadeIn(); void fadeOut(); void fakeFadeOut(); - void setGeometry(const QRectF& rect) Q_DECL_OVERRIDE; + void setGeometry(const QRectF& rect) override; int sortKey() const; void setSortKey(int sortKey); bool isAnimated() const; /** * Sets the opacity on the installed QGraphicsOpacityEffect. * Use this instead of setOpacity(). */ void setGraphicsEffectOpacity(qreal opacity); public Q_SLOTS: void setZoom(qreal); void setZoomToFit(bool); void toggleZoomToFit(); void setZoomToFill(bool); void toggleZoomToFill(); void setPosition(const QPoint&); void hideAndDeleteLater(); Q_SIGNALS: /** * Emitted when the part has finished loading */ void completed(); void previousImageRequested(); void nextImageRequested(); void openUrlRequested(const QUrl&); void openDirUrlRequested(const QUrl&); void captionUpdateRequested(const QString&); void toggleFullScreenRequested(); void videoFinished(); void minimumZoomChanged(qreal); void zoomChanged(qreal); void adapterChanged(); void focused(DocumentView*); void zoomToFitChanged(bool); void zoomToFillChanged(bool); void positionChanged(); void hudTrashClicked(DocumentView*); void hudDeselectClicked(DocumentView*); void fadeInFinished(DocumentView*); void contextMenuRequested(); void currentToolChanged(AbstractRasterImageViewTool*); void isAnimatedChanged(); protected: - void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = nullptr) Q_DECL_OVERRIDE; + void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = nullptr) override; - void resizeEvent(QGraphicsSceneResizeEvent* event) Q_DECL_OVERRIDE; - void mousePressEvent(QGraphicsSceneMouseEvent* event) Q_DECL_OVERRIDE; - void wheelEvent(QGraphicsSceneWheelEvent* event) Q_DECL_OVERRIDE; - void contextMenuEvent(QGraphicsSceneContextMenuEvent* event) Q_DECL_OVERRIDE; - bool sceneEventFilter(QGraphicsItem*, QEvent*) Q_DECL_OVERRIDE; + void resizeEvent(QGraphicsSceneResizeEvent* event) override; + void mousePressEvent(QGraphicsSceneMouseEvent* event) override; + void wheelEvent(QGraphicsSceneWheelEvent* event) override; + void contextMenuEvent(QGraphicsSceneContextMenuEvent* event) override; + bool sceneEventFilter(QGraphicsItem*, QEvent*) override; void dragEnterEvent(QGraphicsSceneDragDropEvent* event) override; void dropEvent(QGraphicsSceneDragDropEvent* event) override; private Q_SLOTS: void finishOpenUrl(); void slotCompleted(); void slotLoadingFailed(); void zoomActualSize(); void zoomIn(QPointF center = QPointF(-1, -1)); void zoomOut(QPointF center = QPointF(-1, -1)); void slotZoomChanged(qreal); void slotBusyChanged(const QUrl&, bool); void emitHudTrashClicked(); void emitHudDeselectClicked(); void emitFocused(); void slotFadeInFinished(); void dragThumbnailLoaded(const KFileItem&, const QPixmap&); void dragThumbnailLoadingFailed(const KFileItem&); private: friend struct DocumentViewPrivate; DocumentViewPrivate* const d; void createAdapterForDocument(); }; } // namespace #endif /* DOCUMENTVIEW_H */ diff --git a/lib/documentview/documentviewcontainer.h b/lib/documentview/documentviewcontainer.h index 66c0b457..f8ba652b 100644 --- a/lib/documentview/documentviewcontainer.h +++ b/lib/documentview/documentviewcontainer.h @@ -1,104 +1,104 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2011 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, Cambridge, MA 02110-1301, USA. */ #ifndef DOCUMENTVIEWCONTAINER_H #define DOCUMENTVIEWCONTAINER_H #include // Local #include // KDE // Qt #include #include namespace Gwenview { class DocumentView; struct DocumentViewContainerPrivate; /** * A container for DocumentViews which will arrange them to make best use of * available space. */ class GWENVIEWLIB_EXPORT DocumentViewContainer : public QGraphicsView { Q_OBJECT public: explicit DocumentViewContainer(QWidget* parent = nullptr); - ~DocumentViewContainer(); + ~DocumentViewContainer() override; /** * Create a DocumentView in the DocumentViewContainer scene */ DocumentView* createView(); /** * Delete view. Note that the view will first be faded to black before * being destroyed. */ void deleteView(DocumentView* view); /** * Immediately delete all views */ void reset(); /** * Returns saved Setup configuration for a previously viewed document */ DocumentView::Setup savedSetup(const QUrl &url) const; /** * Updates setupForUrl hash with latest setup values */ void updateSetup(DocumentView* view); void showMessageWidget(QGraphicsWidget*, Qt::Alignment); /** * Set palette on this and all document views */ void applyPalette(const QPalette& palette); public Q_SLOTS: void updateLayout(); protected: - void showEvent(QShowEvent*) Q_DECL_OVERRIDE; - void resizeEvent(QResizeEvent*) Q_DECL_OVERRIDE; + void showEvent(QShowEvent*) override; + void resizeEvent(QResizeEvent*) override; private: friend class ViewItem; DocumentViewContainerPrivate* const d; private Q_SLOTS: void slotFadeInFinished(DocumentView*); void pretendFadeInFinished(); void slotConfigChanged(); }; } // namespace #endif /* DOCUMENTVIEWCONTAINER_H */ diff --git a/lib/documentview/documentviewcontroller.cpp b/lib/documentview/documentviewcontroller.cpp index 8ccae6f4..895d6398 100644 --- a/lib/documentview/documentviewcontroller.cpp +++ b/lib/documentview/documentviewcontroller.cpp @@ -1,281 +1,281 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2011 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, Cambridge, MA 02110-1301, USA. */ // Self #include "documentviewcontroller.h" // Local #include "abstractdocumentviewadapter.h" #include "documentview.h" #include #include #include // KDE #include #include // Qt #include #include #include #include namespace Gwenview { /** * A simple container which: * - Horizontally center the tool widget * - Provide a darker background */ class ToolContainerContent : public QWidget { public: explicit ToolContainerContent(QWidget* parent = nullptr) : QWidget(parent) , mLayout(new QHBoxLayout(this)) { mLayout->setMargin(0); setAutoFillBackground(true); setBackgroundRole(QPalette::Mid); } void setToolWidget(QWidget* widget) { mLayout->addWidget(widget, 0, Qt::AlignCenter); setFixedHeight(widget->sizeHint().height()); } private: QHBoxLayout* mLayout; }; struct DocumentViewControllerPrivate { DocumentViewController* q; KActionCollection* mActionCollection; DocumentView* mView; ZoomWidget* mZoomWidget; SlideContainer* mToolContainer; ToolContainerContent* mToolContainerContent; QAction * mZoomToFitAction; QAction * mZoomToFillAction; QAction * mActualSizeAction; QAction * mZoomInAction; QAction * mZoomOutAction; QList mActions; void setupActions() { KActionCategory* view = new KActionCategory(i18nc("@title actions category - means actions changing smth in interface", "View"), mActionCollection); - mZoomToFitAction = view->addAction("view_zoom_to_fit"); + mZoomToFitAction = view->addAction(QStringLiteral("view_zoom_to_fit")); view->collection()->setDefaultShortcut(mZoomToFitAction, Qt::Key_F); mZoomToFitAction->setCheckable(true); mZoomToFitAction->setChecked(true); mZoomToFitAction->setText(i18n("Zoom to fit")); - mZoomToFitAction->setIcon(QIcon::fromTheme("zoom-fit-best")); + mZoomToFitAction->setIcon(QIcon::fromTheme(QStringLiteral("zoom-fit-best"))); mZoomToFitAction->setIconText(i18nc("@action:button Zoom to fit, shown in status bar, keep it short please", "Fit")); - mZoomToFillAction = view->addAction("view_zoom_to_fill"); + mZoomToFillAction = view->addAction(QStringLiteral("view_zoom_to_fill")); view->collection()->setDefaultShortcut(mZoomToFillAction, Qt::SHIFT + Qt::Key_F); mZoomToFillAction->setCheckable(true); mZoomToFillAction->setText(i18n("Zoom to fill window by fitting to width or height")); - mZoomToFillAction->setIcon(QIcon::fromTheme("zoom-fit-best")); + mZoomToFillAction->setIcon(QIcon::fromTheme(QStringLiteral("zoom-fit-best"))); mZoomToFillAction->setIconText(i18nc("@action:button Zoom to fill (fit width or height), shown in status bar, keep it short please", "Fill")); mActualSizeAction = view->addAction(KStandardAction::ActualSize); mActualSizeAction->setCheckable(true); - mActualSizeAction->setIcon(QIcon::fromTheme("zoom-original")); + mActualSizeAction->setIcon(QIcon::fromTheme(QStringLiteral("zoom-original"))); mActualSizeAction->setIconText(i18nc("@action:button Zoom to original size, shown in status bar, keep it short please", "100%")); mZoomInAction = view->addAction(KStandardAction::ZoomIn); mZoomOutAction = view->addAction(KStandardAction::ZoomOut); mActions << mZoomToFitAction << mActualSizeAction << mZoomInAction << mZoomOutAction << mZoomToFillAction; } void connectZoomWidget() { if (!mZoomWidget || !mView) { return; } QObject::connect(mZoomWidget, &ZoomWidget::zoomChanged, mView, &DocumentView::setZoom); QObject::connect(mView, &DocumentView::minimumZoomChanged, mZoomWidget, &ZoomWidget::setMinimumZoom); QObject::connect(mView, &DocumentView::zoomChanged, mZoomWidget, &ZoomWidget::setZoom); mZoomWidget->setMinimumZoom(mView->minimumZoom()); mZoomWidget->setZoom(mView->zoom()); } void updateZoomWidgetVisibility() { if (!mZoomWidget) { return; } mZoomWidget->setVisible(mView && mView->canZoom()); } void updateActions() { const bool enabled = mView && mView->isVisible() && mView->canZoom(); Q_FOREACH(QAction * action, mActions) { action->setEnabled(enabled); } } }; DocumentViewController::DocumentViewController(KActionCollection* actionCollection, QObject* parent) : QObject(parent) , d(new DocumentViewControllerPrivate) { d->q = this; d->mActionCollection = actionCollection; d->mView = nullptr; d->mZoomWidget = nullptr; d->mToolContainer = nullptr; d->mToolContainerContent = new ToolContainerContent; d->setupActions(); } DocumentViewController::~DocumentViewController() { delete d; } void DocumentViewController::setView(DocumentView* view) { // Forget old view if (d->mView) { disconnect(d->mView, nullptr, this, nullptr); Q_FOREACH(QAction * action, d->mActions) { disconnect(action, nullptr, d->mView, nullptr); } disconnect(d->mZoomWidget, nullptr, d->mView, nullptr); } // Connect new view d->mView = view; if (!d->mView) { return; } connect(d->mView, &DocumentView::adapterChanged, this, &DocumentViewController::slotAdapterChanged); connect(d->mView, &DocumentView::zoomToFitChanged, this, &DocumentViewController::updateZoomToFitActionFromView); connect(d->mView, &DocumentView::zoomToFillChanged, this, &DocumentViewController::updateZoomToFillActionFromView); connect(d->mView, &DocumentView::currentToolChanged, this, &DocumentViewController::updateTool); connect(d->mZoomToFitAction, &QAction::triggered, d->mView, &DocumentView::toggleZoomToFit); connect(d->mZoomToFillAction, &QAction::triggered, d->mView, &DocumentView::toggleZoomToFill); connect(d->mActualSizeAction, SIGNAL(triggered()), d->mView, SLOT(zoomActualSize())); connect(d->mZoomInAction, SIGNAL(triggered()), d->mView, SLOT(zoomIn())); connect(d->mZoomOutAction, SIGNAL(triggered()), d->mView, SLOT(zoomOut())); d->updateActions(); updateZoomToFitActionFromView(); updateZoomToFillActionFromView(); updateTool(); // Sync zoom widget d->connectZoomWidget(); d->updateZoomWidgetVisibility(); } DocumentView* DocumentViewController::view() const { return d->mView; } void DocumentViewController::setZoomWidget(ZoomWidget* widget) { d->mZoomWidget = widget; d->mZoomWidget->setActions( d->mZoomToFitAction, d->mActualSizeAction, d->mZoomInAction, d->mZoomOutAction, d->mZoomToFillAction ); d->mZoomWidget->setMaximumZoom(qreal(DocumentView::MaximumZoom)); d->connectZoomWidget(); d->updateZoomWidgetVisibility(); } ZoomWidget* DocumentViewController::zoomWidget() const { return d->mZoomWidget; } void DocumentViewController::slotAdapterChanged() { d->updateActions(); d->updateZoomWidgetVisibility(); } void DocumentViewController::updateZoomToFitActionFromView() { d->mZoomToFitAction->setChecked(d->mView->zoomToFit()); } void DocumentViewController::updateZoomToFillActionFromView() { d->mZoomToFillAction->setChecked(d->mView->zoomToFill()); } void DocumentViewController::updateTool() { if (!d->mToolContainer) { return; } AbstractRasterImageViewTool* tool = d->mView->currentTool(); if (tool && tool->widget()) { // Use a QueuedConnection to ensure the size of the view has been // updated by the time the slot is called. connect(d->mToolContainer, &SlideContainer::slidedIn, tool, &AbstractRasterImageViewTool::onWidgetSlidedIn, Qt::QueuedConnection); d->mToolContainerContent->setToolWidget(tool->widget()); d->mToolContainer->slideIn(); } else { d->mToolContainer->slideOut(); } } void DocumentViewController::reset() { setView(nullptr); d->updateActions(); } void DocumentViewController::setToolContainer(SlideContainer* container) { d->mToolContainer = container; container->setContent(d->mToolContainerContent); } } // namespace diff --git a/lib/documentview/loadingindicator.h b/lib/documentview/loadingindicator.h index ed89267f..7b2cd289 100644 --- a/lib/documentview/loadingindicator.h +++ b/lib/documentview/loadingindicator.h @@ -1,61 +1,61 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2011 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, Cambridge, MA 02110-1301, USA. */ #ifndef LOADINGINDICATOR_H #define LOADINGINDICATOR_H // Local // KDE // Qt #include namespace Gwenview { struct LoadingIndicatorPrivate; /** * A graphics widget which shows a spinner */ class LoadingIndicator : public QGraphicsWidget { Q_OBJECT public: explicit LoadingIndicator(QGraphicsItem* parent = nullptr); - ~LoadingIndicator() Q_DECL_OVERRIDE; + ~LoadingIndicator() override; - void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = nullptr) Q_DECL_OVERRIDE; + void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = nullptr) override; - QRectF boundingRect() const Q_DECL_OVERRIDE; + QRectF boundingRect() const override; private Q_SLOTS: void showNextFrame(); protected: - QVariant itemChange(GraphicsItemChange change, const QVariant &value) Q_DECL_OVERRIDE; + QVariant itemChange(GraphicsItemChange change, const QVariant &value) override; private: LoadingIndicatorPrivate* const d; }; } // namespace #endif /* LOADINGINDICATOR_H */ diff --git a/lib/documentview/messageviewadapter.cpp b/lib/documentview/messageviewadapter.cpp index 0636634d..5472f26e 100644 --- a/lib/documentview/messageviewadapter.cpp +++ b/lib/documentview/messageviewadapter.cpp @@ -1,130 +1,130 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 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, Cambridge, MA 02110-1301, USA. */ // Self #include "messageviewadapter.h" // Qt #include #include // KDE #include // Local #include #include namespace Gwenview { struct MessageViewAdapterPrivate : Ui_MessageView { Document::Ptr mDocument; }; MessageViewAdapter::MessageViewAdapter() : d(new MessageViewAdapterPrivate) { QWidget* widget = new QWidget; widget->installEventFilter(this); d->setupUi(widget); d->mMessageWidget->setCloseButtonVisible(false); d->mMessageWidget->setWordWrap(true); setInfoMessage(i18n("No document selected")); widget->setAutoFillBackground(true); widget->setBackgroundRole(QPalette::Base); widget->setForegroundRole(QPalette::Text); QGraphicsProxyWidget* proxy = new QGraphicsProxyWidget; proxy->setWidget(widget); setWidget(proxy); } MessageViewAdapter::~MessageViewAdapter() { delete d; } void MessageViewAdapter::setErrorMessage(const QString& main, const QString& detail) { if (main.isEmpty()) { d->mMessageWidget->hide(); return; } d->mMessageWidget->show(); d->mMessageWidget->setMessageType(KMessageWidget::Error); QString message; if (detail.isEmpty()) { message = main; } else { - message = QString("%1
%2").arg(main).arg(detail); + message = QStringLiteral("%1
%2").arg(main).arg(detail); } d->mMessageWidget->setText(message); } void MessageViewAdapter::setInfoMessage(const QString& message) { if (message.isEmpty()) { d->mMessageWidget->hide(); return; } d->mMessageWidget->show(); d->mMessageWidget->setMessageType(KMessageWidget::Information); d->mMessageWidget->setText(message); } Document::Ptr MessageViewAdapter::document() const { return d->mDocument; } void MessageViewAdapter::setDocument(Document::Ptr doc) { d->mDocument = doc; } bool MessageViewAdapter::eventFilter(QObject*, QEvent* ev) { if (ev->type() == QEvent::KeyPress) { QKeyEvent* event = static_cast(ev); if (event->modifiers() != Qt::NoModifier) { return false; } switch (event->key()) { case Qt::Key_Left: case Qt::Key_Up: previousImageRequested(); break; case Qt::Key_Right: case Qt::Key_Down: nextImageRequested(); break; default: break; } } return false; } } // namespace diff --git a/lib/documentview/messageviewadapter.h b/lib/documentview/messageviewadapter.h index 0676a5bc..4c89c897 100644 --- a/lib/documentview/messageviewadapter.h +++ b/lib/documentview/messageviewadapter.h @@ -1,64 +1,64 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 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, Cambridge, MA 02110-1301, USA. */ #ifndef MESSAGEVIEWADAPTER_H #define MESSAGEVIEWADAPTER_H // Qt // KDE // Local #include namespace Gwenview { struct MessageViewAdapterPrivate; class MessageViewAdapter : public AbstractDocumentViewAdapter { Q_OBJECT public: MessageViewAdapter(); - ~MessageViewAdapter() Q_DECL_OVERRIDE; + ~MessageViewAdapter() override; - MimeTypeUtils::Kind kind() const Q_DECL_OVERRIDE + MimeTypeUtils::Kind kind() const override { return MimeTypeUtils::KIND_UNKNOWN; } - Document::Ptr document() const Q_DECL_OVERRIDE; + Document::Ptr document() const override; - void setDocument(Document::Ptr) Q_DECL_OVERRIDE; + void setDocument(Document::Ptr) override; void setInfoMessage(const QString&); void setErrorMessage(const QString& main, const QString& detail = QString()); protected: - bool eventFilter(QObject*, QEvent*) Q_DECL_OVERRIDE; + bool eventFilter(QObject*, QEvent*) override; private: MessageViewAdapterPrivate* const d; }; } // namespace #endif /* MESSAGEVIEWADAPTER_H */ diff --git a/lib/documentview/rasterimageview.h b/lib/documentview/rasterimageview.h index 8f7231fc..c1fc7e9d 100644 --- a/lib/documentview/rasterimageview.h +++ b/lib/documentview/rasterimageview.h @@ -1,88 +1,88 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2011 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, Cambridge, MA 02110-1301, USA. */ #ifndef RASTERIMAGEVIEW_H #define RASTERIMAGEVIEW_H #include // Local #include #include // KDE class QGraphicsSceneHoverEvent; namespace Gwenview { class AbstractRasterImageViewTool; struct RasterImageViewPrivate; class GWENVIEWLIB_EXPORT RasterImageView : public AbstractImageView { Q_OBJECT public: explicit RasterImageView(QGraphicsItem* parent = nullptr); - ~RasterImageView() Q_DECL_OVERRIDE; + ~RasterImageView() override; - void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) Q_DECL_OVERRIDE; + void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override; void setCurrentTool(AbstractRasterImageViewTool* tool); AbstractRasterImageViewTool* currentTool() const; void setAlphaBackgroundMode(AlphaBackgroundMode mode) override; void setAlphaBackgroundColor(const QColor& color) override; void setRenderingIntent(const RenderingIntent::Enum& renderingIntent); Q_SIGNALS: void currentToolChanged(AbstractRasterImageViewTool*); void imageRectUpdated(); protected: - void loadFromDocument() Q_DECL_OVERRIDE; - void onZoomChanged() Q_DECL_OVERRIDE; - void onImageOffsetChanged() Q_DECL_OVERRIDE; - void onScrollPosChanged(const QPointF& oldPos) Q_DECL_OVERRIDE; - void resizeEvent(QGraphicsSceneResizeEvent* event) Q_DECL_OVERRIDE; - void mousePressEvent(QGraphicsSceneMouseEvent* event) Q_DECL_OVERRIDE; - void mouseMoveEvent(QGraphicsSceneMouseEvent* event) Q_DECL_OVERRIDE; - void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) Q_DECL_OVERRIDE; - void wheelEvent(QGraphicsSceneWheelEvent* event) Q_DECL_OVERRIDE; - void keyPressEvent(QKeyEvent* event) Q_DECL_OVERRIDE; - void keyReleaseEvent(QKeyEvent* event) Q_DECL_OVERRIDE; - void hoverMoveEvent(QGraphicsSceneHoverEvent*) Q_DECL_OVERRIDE; + void loadFromDocument() override; + void onZoomChanged() override; + void onImageOffsetChanged() override; + void onScrollPosChanged(const QPointF& oldPos) override; + void resizeEvent(QGraphicsSceneResizeEvent* event) override; + void mousePressEvent(QGraphicsSceneMouseEvent* event) override; + void mouseMoveEvent(QGraphicsSceneMouseEvent* event) override; + void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) override; + void wheelEvent(QGraphicsSceneWheelEvent* event) override; + void keyPressEvent(QKeyEvent* event) override; + void keyReleaseEvent(QKeyEvent* event) override; + void hoverMoveEvent(QGraphicsSceneHoverEvent*) override; private Q_SLOTS: void slotDocumentMetaInfoLoaded(); void slotDocumentIsAnimatedUpdated(); void finishSetDocument(); void updateFromScaler(int, int, const QImage&); void updateImageRect(const QRect& imageRect); void updateBuffer(const QRegion& region = QRegion()); private: RasterImageViewPrivate* const d; }; } // namespace #endif /* RASTERIMAGEVIEW_H */ diff --git a/lib/documentview/rasterimageviewadapter.h b/lib/documentview/rasterimageviewadapter.h index 060738f1..64986b86 100644 --- a/lib/documentview/rasterimageviewadapter.h +++ b/lib/documentview/rasterimageviewadapter.h @@ -1,97 +1,97 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 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, Cambridge, MA 02110-1301, USA. */ #ifndef RASTERIMAGEVIEWADAPTER_H #define RASTERIMAGEVIEWADAPTER_H #include // Qt // KDE // Local #include namespace Gwenview { struct RasterImageViewAdapterPrivate; class GWENVIEWLIB_EXPORT RasterImageViewAdapter : public AbstractDocumentViewAdapter { Q_OBJECT public: RasterImageViewAdapter(); - ~RasterImageViewAdapter() Q_DECL_OVERRIDE; + ~RasterImageViewAdapter() override; - QCursor cursor() const Q_DECL_OVERRIDE; + QCursor cursor() const override; - void setCursor(const QCursor&) Q_DECL_OVERRIDE; + void setCursor(const QCursor&) override; - MimeTypeUtils::Kind kind() const Q_DECL_OVERRIDE + MimeTypeUtils::Kind kind() const override { return MimeTypeUtils::KIND_RASTER_IMAGE; } - bool canZoom() const Q_DECL_OVERRIDE + bool canZoom() const override { return true; } - void setZoomToFit(bool) Q_DECL_OVERRIDE; + void setZoomToFit(bool) override; - void setZoomToFill(bool on, const QPointF& center) Q_DECL_OVERRIDE; + void setZoomToFill(bool on, const QPointF& center) override; - bool zoomToFit() const Q_DECL_OVERRIDE; + bool zoomToFit() const override; - bool zoomToFill() const Q_DECL_OVERRIDE; + bool zoomToFill() const override; - qreal zoom() const Q_DECL_OVERRIDE; + qreal zoom() const override; - void setZoom(qreal zoom, const QPointF& center) Q_DECL_OVERRIDE; + void setZoom(qreal zoom, const QPointF& center) override; - qreal computeZoomToFit() const Q_DECL_OVERRIDE; + qreal computeZoomToFit() const override; - qreal computeZoomToFill() const Q_DECL_OVERRIDE; + qreal computeZoomToFill() const override; - Document::Ptr document() const Q_DECL_OVERRIDE; + Document::Ptr document() const override; - void setDocument(Document::Ptr) Q_DECL_OVERRIDE; + void setDocument(Document::Ptr) override; - void loadConfig() Q_DECL_OVERRIDE; + void loadConfig() override; - RasterImageView* rasterImageView() const Q_DECL_OVERRIDE; + RasterImageView* rasterImageView() const override; virtual AbstractImageView* imageView() const override; - QPointF scrollPos() const Q_DECL_OVERRIDE; - void setScrollPos(const QPointF& pos) Q_DECL_OVERRIDE; + QPointF scrollPos() const override; + void setScrollPos(const QPointF& pos) override; - QRectF visibleDocumentRect() const Q_DECL_OVERRIDE; + QRectF visibleDocumentRect() const override; private Q_SLOTS: void slotLoadingFailed(); private: RasterImageViewAdapterPrivate* const d; }; } // namespace #endif /* RASTERIMAGEVIEWADAPTER_H */ diff --git a/lib/documentview/svgviewadapter.h b/lib/documentview/svgviewadapter.h index e503bbf5..4dd7ab78 100644 --- a/lib/documentview/svgviewadapter.h +++ b/lib/documentview/svgviewadapter.h @@ -1,125 +1,125 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 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, Cambridge, MA 02110-1301, USA. */ #ifndef SVGVIEWADAPTER_H #define SVGVIEWADAPTER_H #include // Qt #include // KDE // Local #include #include class QGraphicsSvgItem; namespace Gwenview { class SvgImageView : public AbstractImageView { Q_OBJECT public: explicit SvgImageView(QGraphicsItem* parent = nullptr); - void setAlphaBackgroundMode(AlphaBackgroundMode mode) Q_DECL_OVERRIDE; - void setAlphaBackgroundColor(const QColor& color) Q_DECL_OVERRIDE; + void setAlphaBackgroundMode(AlphaBackgroundMode mode) override; + void setAlphaBackgroundColor(const QColor& color) override; protected: - void loadFromDocument() Q_DECL_OVERRIDE; - void onZoomChanged() Q_DECL_OVERRIDE; - void onImageOffsetChanged() Q_DECL_OVERRIDE; - void onScrollPosChanged(const QPointF& oldPos) Q_DECL_OVERRIDE; + void loadFromDocument() override; + void onZoomChanged() override; + void onImageOffsetChanged() override; + void onScrollPosChanged(const QPointF& oldPos) override; private Q_SLOTS: void finishLoadFromDocument(); private: QGraphicsSvgItem* mSvgItem; AbstractImageView::AlphaBackgroundMode mAlphaBackgroundMode; QColor mAlphaBackgroundColor; bool mImageFullyLoaded; void adjustItemPos(); void drawAlphaBackground(QPainter* painter); void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override; }; struct SvgViewAdapterPrivate; class GWENVIEWLIB_EXPORT SvgViewAdapter : public AbstractDocumentViewAdapter { Q_OBJECT public: SvgViewAdapter(); ~SvgViewAdapter(); - QCursor cursor() const Q_DECL_OVERRIDE; + QCursor cursor() const override; - void setCursor(const QCursor&) Q_DECL_OVERRIDE; + void setCursor(const QCursor&) override; - void setDocument(Document::Ptr) Q_DECL_OVERRIDE; + void setDocument(Document::Ptr) override; - Document::Ptr document() const Q_DECL_OVERRIDE; + Document::Ptr document() const override; - void loadConfig() Q_DECL_OVERRIDE; + void loadConfig() override; - MimeTypeUtils::Kind kind() const Q_DECL_OVERRIDE + MimeTypeUtils::Kind kind() const override { return MimeTypeUtils::KIND_SVG_IMAGE; } - bool canZoom() const Q_DECL_OVERRIDE + bool canZoom() const override { return true; } - void setZoomToFit(bool) Q_DECL_OVERRIDE; + void setZoomToFit(bool) override; - void setZoomToFill(bool on, const QPointF& center) Q_DECL_OVERRIDE; + void setZoomToFill(bool on, const QPointF& center) override; - bool zoomToFit() const Q_DECL_OVERRIDE; + bool zoomToFit() const override; - bool zoomToFill() const Q_DECL_OVERRIDE; + bool zoomToFill() const override; - qreal zoom() const Q_DECL_OVERRIDE; + qreal zoom() const override; - void setZoom(qreal /*zoom*/, const QPointF& /*center*/ = QPointF(-1, -1)) Q_DECL_OVERRIDE; + void setZoom(qreal /*zoom*/, const QPointF& /*center*/ = QPointF(-1, -1)) override; - qreal computeZoomToFit() const Q_DECL_OVERRIDE; + qreal computeZoomToFit() const override; - qreal computeZoomToFill() const Q_DECL_OVERRIDE; + qreal computeZoomToFill() const override; - QPointF scrollPos() const Q_DECL_OVERRIDE; - void setScrollPos(const QPointF& pos) Q_DECL_OVERRIDE; + QPointF scrollPos() const override; + void setScrollPos(const QPointF& pos) override; virtual QRectF visibleDocumentRect() const override; virtual AbstractImageView* imageView() const override; private: SvgViewAdapterPrivate* const d; }; } // namespace #endif /* SVGVIEWADAPTER_H */ diff --git a/lib/documentview/videoviewadapter.cpp b/lib/documentview/videoviewadapter.cpp index b5af9b5f..724433a1 100644 --- a/lib/documentview/videoviewadapter.cpp +++ b/lib/documentview/videoviewadapter.cpp @@ -1,344 +1,344 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2009 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, Cambridge, MA 02110-1301, USA. */ // Self #include "videoviewadapter.h" // Qt #include #include #include #include #include #include #include #include #include #include #include // KDE // Local #include #include #include #include #include namespace Gwenview { struct VideoViewAdapterPrivate { VideoViewAdapter* q; Phonon::MediaObject* mMediaObject; Phonon::VideoWidget* mVideoWidget; Phonon::AudioOutput* mAudioOutput; HudWidget* mHud; GraphicsWidgetFloater* mFloater; HudSlider* mSeekSlider; QTime mLastSeekSliderActionTime; QAction* mPlayPauseAction; QAction* mMuteAction; HudSlider* mVolumeSlider; QTime mLastVolumeSliderChangeTime; Document::Ptr mDocument; void setupActions() { mPlayPauseAction = new QAction(q); mPlayPauseAction->setShortcut(Qt::Key_P); QObject::connect(mPlayPauseAction, &QAction::triggered, q, &VideoViewAdapter::slotPlayPauseClicked); QObject::connect(mMediaObject, &Phonon::MediaObject::stateChanged, q, &VideoViewAdapter::updatePlayUi); mMuteAction = new QAction(q); mMuteAction->setShortcut(Qt::Key_M); QObject::connect(mMuteAction, &QAction::triggered, q, &VideoViewAdapter::slotMuteClicked); QObject::connect(mAudioOutput, &Phonon::AudioOutput::mutedChanged, q, &VideoViewAdapter::updateMuteAction); } void setupHud(QGraphicsWidget* parent) { // Play/Pause HudButton* playPauseButton = new HudButton; playPauseButton->setDefaultAction(mPlayPauseAction); // Seek mSeekSlider = new HudSlider; mSeekSlider->setPageStep(5000); mSeekSlider->setSingleStep(200); QObject::connect(mSeekSlider, &HudSlider::actionTriggered, q, &VideoViewAdapter::slotSeekSliderActionTriggered); QObject::connect(mMediaObject, &Phonon::MediaObject::tick, q, &VideoViewAdapter::slotTicked); QObject::connect(mMediaObject, &Phonon::MediaObject::totalTimeChanged, q, &VideoViewAdapter::updatePlayUi); QObject::connect(mMediaObject, &Phonon::MediaObject::seekableChanged, q, &VideoViewAdapter::updatePlayUi); // Mute HudButton* muteButton = new HudButton; muteButton->setDefaultAction(mMuteAction); // Volume mVolumeSlider = new HudSlider; mVolumeSlider->setMinimumWidth(100); mVolumeSlider->setRange(0, 100); mVolumeSlider->setPageStep(5); mVolumeSlider->setSingleStep(1); QObject::connect(mVolumeSlider, &HudSlider::valueChanged, q, &VideoViewAdapter::slotVolumeSliderChanged); QObject::connect(mAudioOutput, &Phonon::AudioOutput::volumeChanged, q, &VideoViewAdapter::slotOutputVolumeChanged); // Layout QGraphicsWidget* hudContent = new QGraphicsWidget; QGraphicsLinearLayout* layout = new QGraphicsLinearLayout(hudContent); layout->addItem(playPauseButton); layout->addItem(mSeekSlider); layout->setStretchFactor(mSeekSlider, 5); layout->addItem(muteButton); layout->addItem(mVolumeSlider); layout->setStretchFactor(mVolumeSlider, 1); // Create hud mHud = new HudWidget(parent); mHud->init(hudContent, HudWidget::OptionNone); mHud->setZValue(1); // Init floater mFloater = new GraphicsWidgetFloater(parent); mFloater->setChildWidget(mHud); mFloater->setAlignment(Qt::AlignJustify | Qt::AlignBottom); } bool isPlaying() const { switch (mMediaObject->state()) { case Phonon::PlayingState: case Phonon::BufferingState: return true; default: return false; } } void updateHudVisibility(int yPos) { const int floaterY = mVideoWidget->height() - mFloater->verticalMargin() - mHud->effectiveSizeHint(Qt::MinimumSize).height() * 3 / 2; if (yPos < floaterY) { mHud->fadeOut(); } else { mHud->fadeIn(); } } void keyPressEvent(QKeyEvent* event) { if (event->modifiers() != Qt::NoModifier) { return; } switch (event->key()) { case Qt::Key_Left: mSeekSlider->triggerAction(QAbstractSlider::SliderSingleStepSub); break; case Qt::Key_Right: mSeekSlider->triggerAction(QAbstractSlider::SliderSingleStepAdd); break; case Qt::Key_Up: q->previousImageRequested(); break; case Qt::Key_Down: q->nextImageRequested(); break; default: break; } } }; /** * This is a workaround for a bug in QGraphicsProxyWidget: it does not forward * double-click events to the proxy-fied widget. * * QGraphicsProxyWidget::mouseDoubleClickEvent() correctly forwards the event * to its QWidget, but it is never called. This is because for it to be called, * the implementation of mousePressEvent() must call * QGraphicsItem::mousePressEvent() but it does not. */ class DoubleClickableProxyWidget : public QGraphicsProxyWidget { protected: - void mousePressEvent(QGraphicsSceneMouseEvent* event) Q_DECL_OVERRIDE + void mousePressEvent(QGraphicsSceneMouseEvent* event) override { QGraphicsWidget::mousePressEvent(event); } }; VideoViewAdapter::VideoViewAdapter() : d(new VideoViewAdapterPrivate) { d->q = this; d->mMediaObject = new Phonon::MediaObject(this); d->mMediaObject->setTickInterval(350); connect(d->mMediaObject, &Phonon::MediaObject::finished, this, &VideoViewAdapter::videoFinished); d->mVideoWidget = new Phonon::VideoWidget; d->mVideoWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); d->mVideoWidget->setAttribute(Qt::WA_Hover); d->mVideoWidget->installEventFilter(this); Phonon::createPath(d->mMediaObject, d->mVideoWidget); d->mAudioOutput = new Phonon::AudioOutput(Phonon::VideoCategory, this); Phonon::createPath(d->mMediaObject, d->mAudioOutput); QGraphicsProxyWidget* proxy = new DoubleClickableProxyWidget; proxy->setFlag(QGraphicsItem::ItemIsSelectable); // Needed for doubleclick to work proxy->setWidget(d->mVideoWidget); proxy->setAcceptHoverEvents(true); setWidget(proxy); d->setupActions(); d->setupHud(proxy); updatePlayUi(); updateMuteAction(); } VideoViewAdapter::~VideoViewAdapter() { // This prevents a memory leak that can occur after switching // to the next/previous video. For details see: // https://git.reviewboard.kde.org/r/108070/ d->mMediaObject->stop(); delete d; } void VideoViewAdapter::setDocument(Document::Ptr doc) { d->mHud->show(); d->mDocument = doc; d->mMediaObject->setCurrentSource(d->mDocument->url()); d->mMediaObject->play(); // If we do not use a queued connection, the signal arrives too early, // preventing the listing of the dir content when Gwenview is started with // a video as an argument. QMetaObject::invokeMethod(this, "completed", Qt::QueuedConnection); } Document::Ptr VideoViewAdapter::document() const { return d->mDocument; } void VideoViewAdapter::slotPlayPauseClicked() { if (d->isPlaying()) { d->mMediaObject->pause(); } else { d->mMediaObject->play(); } } void VideoViewAdapter::slotMuteClicked() { d->mAudioOutput->setMuted(!d->mAudioOutput->isMuted()); } bool VideoViewAdapter::eventFilter(QObject*, QEvent* event) { if (event->type() == QEvent::MouseMove) { d->updateHudVisibility(static_cast(event)->y()); } else if (event->type() == QEvent::KeyPress) { d->keyPressEvent(static_cast(event)); } else if (event->type() == QEvent::MouseButtonDblClick) { if (static_cast(event)->modifiers() == Qt::NoModifier) { toggleFullScreenRequested(); } } return false; } void VideoViewAdapter::updatePlayUi() { if (d->isPlaying()) { - d->mPlayPauseAction->setIcon(QIcon::fromTheme("media-playback-pause")); + d->mPlayPauseAction->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-pause"))); } else { - d->mPlayPauseAction->setIcon(QIcon::fromTheme("media-playback-start")); + d->mPlayPauseAction->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-start"))); } d->mLastSeekSliderActionTime.restart(); d->mSeekSlider->setRange(0, d->mMediaObject->totalTime()); switch (d->mMediaObject->state()) { case Phonon::PlayingState: case Phonon::BufferingState: case Phonon::PausedState: d->mSeekSlider->setEnabled(true); break; case Phonon::StoppedState: case Phonon::LoadingState: case Phonon::ErrorState: d->mSeekSlider->setEnabled(false); d->mSeekSlider->setValue(0); break; } } void VideoViewAdapter::updateMuteAction() { d->mMuteAction->setIcon( - QIcon::fromTheme(d->mAudioOutput->isMuted() ? "player-volume-muted" : "player-volume") + QIcon::fromTheme(d->mAudioOutput->isMuted() ? QStringLiteral("player-volume-muted") : QStringLiteral("player-volume")) ); } void VideoViewAdapter::slotVolumeSliderChanged(int value) { d->mLastVolumeSliderChangeTime.restart(); d->mAudioOutput->setVolume(value / 100.); } void VideoViewAdapter::slotOutputVolumeChanged(qreal value) { if (d->mLastVolumeSliderChangeTime.isValid() && d->mLastVolumeSliderChangeTime.elapsed() < 2000) { return; } d->mVolumeSlider->setValue(qRound(value * 100)); } void VideoViewAdapter::slotSeekSliderActionTriggered(int /*action*/) { d->mLastSeekSliderActionTime.restart(); d->mMediaObject->seek(d->mSeekSlider->sliderPosition()); } void VideoViewAdapter::slotTicked(qint64 value) { if (d->mLastSeekSliderActionTime.isValid() && d->mLastSeekSliderActionTime.elapsed() < 2000) { return; } if (!d->mSeekSlider->isSliderDown()) { d->mSeekSlider->setValue(value); } } } // namespace diff --git a/lib/documentview/videoviewadapter.h b/lib/documentview/videoviewadapter.h index e2c67a62..de84f6ee 100644 --- a/lib/documentview/videoviewadapter.h +++ b/lib/documentview/videoviewadapter.h @@ -1,76 +1,76 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2009 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, Cambridge, MA 02110-1301, USA. */ #ifndef VIDEOVIEWADAPTER_H #define VIDEOVIEWADAPTER_H #include // Qt // KDE // Local #include namespace Gwenview { struct VideoViewAdapterPrivate; class GWENVIEWLIB_EXPORT VideoViewAdapter : public AbstractDocumentViewAdapter { Q_OBJECT public: VideoViewAdapter(); - ~VideoViewAdapter() Q_DECL_OVERRIDE; + ~VideoViewAdapter() override; - MimeTypeUtils::Kind kind() const Q_DECL_OVERRIDE + MimeTypeUtils::Kind kind() const override { return MimeTypeUtils::KIND_VIDEO; } - Document::Ptr document() const Q_DECL_OVERRIDE; + Document::Ptr document() const override; - void setDocument(Document::Ptr) Q_DECL_OVERRIDE; + void setDocument(Document::Ptr) override; Q_SIGNALS: void videoFinished(); protected: - bool eventFilter(QObject*, QEvent*) Q_DECL_OVERRIDE; + bool eventFilter(QObject*, QEvent*) override; private Q_SLOTS: void slotPlayPauseClicked(); void updatePlayUi(); void slotMuteClicked(); void updateMuteAction(); void slotVolumeSliderChanged(int); void slotOutputVolumeChanged(qreal); void slotSeekSliderActionTriggered(int); void slotTicked(qint64); private: friend struct VideoViewAdapterPrivate; VideoViewAdapterPrivate* const d; }; } // namespace #endif /* VIDEOVIEWADAPTER_H */ diff --git a/lib/eventwatcher.h b/lib/eventwatcher.h index 8ae5800d..bdab2e4e 100644 --- a/lib/eventwatcher.h +++ b/lib/eventwatcher.h @@ -1,60 +1,60 @@ /* Gwenview: an image viewer Copyright 2009 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. */ #ifndef EVENTWATCHER_H #define EVENTWATCHER_H // Qt #include #include #include // Local #include "gwenviewlib_export.h" namespace Gwenview { /** * This class emits a signal when some events are triggered on a watched * object. */ class GWENVIEWLIB_EXPORT EventWatcher : public QObject { Q_OBJECT public: EventWatcher(QObject* watched, const QList& eventTypes); static EventWatcher* install(QObject* watched, const QList& eventTypes, QObject* receiver, const char* slot); static EventWatcher* install(QObject* watched, QEvent::Type eventType, QObject* receiver, const char* slot); Q_SIGNALS: void eventTriggered(QEvent*); protected: - bool eventFilter(QObject*, QEvent* event) Q_DECL_OVERRIDE; + bool eventFilter(QObject*, QEvent* event) override; private: QList mEventTypes; }; } #endif /* EVENTWATCHER_H */ diff --git a/lib/exiv2imageloader.cpp b/lib/exiv2imageloader.cpp index c298c9c3..f2830f81 100644 --- a/lib/exiv2imageloader.cpp +++ b/lib/exiv2imageloader.cpp @@ -1,91 +1,91 @@ // 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 "exiv2imageloader.h" // Qt #include #include #include // KDE // Exiv2 #include #include // Local namespace Gwenview { struct Exiv2ImageLoaderPrivate { Exiv2::Image::AutoPtr mImage; QString mErrorMessage; }; Exiv2ImageLoader::Exiv2ImageLoader() : d(new Exiv2ImageLoaderPrivate) { } Exiv2ImageLoader::~Exiv2ImageLoader() { delete d; } bool Exiv2ImageLoader::load(const QString& filePath) { QByteArray filePathByteArray = QFile::encodeName(filePath); try { d->mImage = Exiv2::ImageFactory::open(filePathByteArray.constData()); d->mImage->readMetadata(); } catch (const Exiv2::Error& error) { - d->mErrorMessage = error.what(); + d->mErrorMessage = QString::fromUtf8(error.what()); return false; } return true; } bool Exiv2ImageLoader::load(const QByteArray& data) { try { d->mImage = Exiv2::ImageFactory::open((unsigned char*)data.constData(), data.size()); d->mImage->readMetadata(); } catch (const Exiv2::Error& error) { - d->mErrorMessage = error.what(); + d->mErrorMessage = QString::fromUtf8(error.what()); return false; } return true; } QString Exiv2ImageLoader::errorMessage() const { return d->mErrorMessage; } Exiv2::Image::AutoPtr Exiv2ImageLoader::popImage() { return d->mImage; } } // namespace diff --git a/lib/flowlayout.h b/lib/flowlayout.h index 8ea68809..d6984f08 100644 --- a/lib/flowlayout.h +++ b/lib/flowlayout.h @@ -1,70 +1,70 @@ /**************************************************************************** ** ** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Qt Software Information (qt-info@nokia.com) ** ** This file is part of the example classes of the Qt Toolkit. ** ** Commercial Usage ** Licensees holding valid Qt Commercial licenses may use this file in ** accordance with the Qt Commercial License Agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Nokia. ** ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License versions 2.0 or 3.0 as published by the Free ** Software Foundation and appearing in the file LICENSE.GPL included in ** the packaging of this file. Please review the following information ** to ensure GNU General Public Licensing requirements will be met: ** http://www.fsf.org/licensing/licenses/info/GPLv2.html and ** http://www.gnu.org/copyleft/gpl.html. In addition, as a special ** exception, Nokia gives you certain additional rights. These rights ** are described in the Nokia Qt GPL Exception version 1.3, included in ** the file GPL_EXCEPTION.txt in this package. ** ** Qt for Windows(R) Licensees ** As a special exception, Nokia, as the sole copyright holder for Qt ** Designer, grants users of the Qt/Eclipse Integration plug-in the ** right for the Qt/Eclipse Integration to link to functionality ** provided by Qt Designer and its related libraries. ** ** If you are unsure which license is appropriate for your use, please ** contact the sales department at qt-sales@nokia.com. ** ****************************************************************************/ #ifndef FLOWLAYOUT_H #define FLOWLAYOUT_H #include #include #include class GWENVIEWLIB_EXPORT FlowLayout : public QLayout { public: explicit FlowLayout(QWidget *parent, int margin = 0, int spacing = -1); FlowLayout(int spacing = -1); - ~FlowLayout() Q_DECL_OVERRIDE; + ~FlowLayout() override; - void addItem(QLayoutItem *item) Q_DECL_OVERRIDE; - Qt::Orientations expandingDirections() const Q_DECL_OVERRIDE; - bool hasHeightForWidth() const Q_DECL_OVERRIDE; - int heightForWidth(int) const Q_DECL_OVERRIDE; - int count() const Q_DECL_OVERRIDE; - QLayoutItem *itemAt(int index) const Q_DECL_OVERRIDE; - QSize minimumSize() const Q_DECL_OVERRIDE; - void setGeometry(const QRect &rect) Q_DECL_OVERRIDE; - QSize sizeHint() const Q_DECL_OVERRIDE; - QLayoutItem *takeAt(int index) Q_DECL_OVERRIDE; + void addItem(QLayoutItem *item) override; + Qt::Orientations expandingDirections() const override; + bool hasHeightForWidth() const override; + int heightForWidth(int) const override; + int count() const override; + QLayoutItem *itemAt(int index) const override; + QSize minimumSize() const override; + void setGeometry(const QRect &rect) override; + QSize sizeHint() const override; + QLayoutItem *takeAt(int index) override; private: int doLayout(const QRect &rect, bool testOnly) const; QList itemList; }; #endif diff --git a/lib/fullscreenbar.h b/lib/fullscreenbar.h index dd25cb16..b08c2dd2 100644 --- a/lib/fullscreenbar.h +++ b/lib/fullscreenbar.h @@ -1,71 +1,71 @@ // 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. */ #ifndef FULLSCREENBAR_H #define FULLSCREENBAR_H #include // Qt #include // KDE // Local class QEvent; namespace Gwenview { struct FullScreenBarPrivate; class GWENVIEWLIB_EXPORT FullScreenBar : public QFrame { Q_OBJECT public: explicit FullScreenBar(QWidget* parent); - ~FullScreenBar() Q_DECL_OVERRIDE; + ~FullScreenBar() override; void setActivated(bool); void setAutoHidingEnabled(bool); void setEdgeTriggerEnabled(bool); - QSize sizeHint() const Q_DECL_OVERRIDE; + QSize sizeHint() const override; public Q_SLOTS: void slideIn(); void slideOut(); private Q_SLOTS: void slotAutoHideCursorTimeout(); void moveBar(qreal); void delayedInstallEventFilter(); protected: - bool eventFilter(QObject*, QEvent*) Q_DECL_OVERRIDE; + bool eventFilter(QObject*, QEvent*) override; private: FullScreenBarPrivate* const d; }; } // namespace #endif /* FULLSCREENBAR_H */ diff --git a/lib/graphicswidgetfloater.h b/lib/graphicswidgetfloater.h index f18aaab0..163ab46f 100644 --- a/lib/graphicswidgetfloater.h +++ b/lib/graphicswidgetfloater.h @@ -1,74 +1,74 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 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, Cambridge, MA 02110-1301, USA. */ #ifndef GRAPHICSWIDGETFLOATER_H #define GRAPHICSWIDGETFLOATER_H #include // Qt #include // KDE // Local class QGraphicsWidget; namespace Gwenview { struct GraphicsWidgetFloaterPrivate; /** * This helper object makes it possible to place a widget (the child) over * another (the parent), ensuring the child remains aligned as specified by * setAlignment() whenever either widget get resized. */ class GWENVIEWLIB_EXPORT GraphicsWidgetFloater : public QObject { Q_OBJECT public: GraphicsWidgetFloater(QGraphicsWidget* parent); - ~GraphicsWidgetFloater() Q_DECL_OVERRIDE; + ~GraphicsWidgetFloater() override; void setChildWidget(QGraphicsWidget*); void setAlignment(Qt::Alignment); void setHorizontalMargin(int); int horizontalMargin() const; void setVerticalMargin(int); int verticalMargin() const; protected: - bool eventFilter(QObject*, QEvent*) Q_DECL_OVERRIDE; + bool eventFilter(QObject*, QEvent*) override; private Q_SLOTS: void slotChildVisibilityChanged(); private: GraphicsWidgetFloaterPrivate* const d; }; } // namespace #endif /* GRAPHICSWIDGETFLOATER_H */ diff --git a/lib/historymodel.cpp b/lib/historymodel.cpp index d966dbc3..9cec1a4d 100644 --- a/lib/historymodel.cpp +++ b/lib/historymodel.cpp @@ -1,261 +1,261 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2009 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, Cambridge, MA 02110-1301, USA. */ // Self #include "historymodel.h" // Qt #include #include #include #include #include #include #include // KDE #include #include #include #include #include #include #include // Local #include namespace Gwenview { struct HistoryItem : public QStandardItem { void save() const { KConfig config(mConfigPath, KConfig::SimpleConfig); KConfigGroup group(&config, "general"); group.writeEntry("url", mUrl.toString()); group.writeEntry("dateTime", mDateTime.toString(Qt::ISODate)); config.sync(); } static HistoryItem* create(const QUrl &url, const QDateTime& dateTime, const QString& storageDir) { if (!QDir().mkpath(storageDir)) { qCritical() << "Could not create history dir" << storageDir; return nullptr; } QTemporaryFile file(storageDir + QStringLiteral("/gvhistoryXXXXXXrc")); file.setAutoRemove(false); if (!file.open()) { qCritical() << "Could not create history file"; return nullptr; } HistoryItem* item = new HistoryItem(url, dateTime, file.fileName()); item->save(); return item; } static HistoryItem* load(const QString& fileName) { KConfig config(fileName, KConfig::SimpleConfig); KConfigGroup group(&config, "general"); QUrl url(group.readEntry("url")); if (!url.isValid()) { qCritical() << "Invalid url" << url; return nullptr; } QDateTime dateTime = QDateTime::fromString(group.readEntry("dateTime"), Qt::ISODate); if (!dateTime.isValid()) { qCritical() << "Invalid dateTime" << dateTime; return nullptr; } return new HistoryItem(url, dateTime, fileName); } QUrl url() const { return mUrl; } QDateTime dateTime() const { return mDateTime; } void setDateTime(const QDateTime& dateTime) { if (mDateTime != dateTime) { mDateTime = dateTime; save(); } } void unlink() { QFile::remove(mConfigPath); } private: QUrl mUrl; QDateTime mDateTime; QString mConfigPath; HistoryItem(const QUrl &url, const QDateTime& dateTime, const QString& configPath) : mUrl(url) , mDateTime(dateTime) , mConfigPath(configPath) { QString text(mUrl.toDisplayString(QUrl::PreferLocalFile)); #ifdef Q_OS_UNIX // shorten home directory, but avoid showing a cryptic "~/" if (text.length() > QDir::homePath().length() + 1) { - text.replace(QRegularExpression('^' + QDir::homePath()), "~"); + text.replace(QRegularExpression('^' + QDir::homePath()), QStringLiteral("~")); } #endif setText(text); QMimeDatabase db; const QString iconName = db.mimeTypeForUrl(mUrl).iconName(); setIcon(QIcon::fromTheme(iconName)); setData(mUrl, KFilePlacesModel::UrlRole); KFileItem fileItem(mUrl); setData(QVariant(fileItem), KDirModel::FileItemRole); const QString date = KFormat().formatRelativeDateTime(mDateTime, QLocale::LongFormat); setData(i18n("Last visited: %1", date), Qt::ToolTipRole); } - bool operator<(const QStandardItem& other) const Q_DECL_OVERRIDE { + bool operator<(const QStandardItem& other) const override { return mDateTime > static_cast(&other)->mDateTime; } }; struct HistoryModelPrivate { HistoryModel* q; QString mStorageDir; int mMaxCount; QMap mHistoryItemForUrl; void load() { QDir dir(mStorageDir); if (!dir.exists()) { return; } - Q_FOREACH(const QString & name, dir.entryList(QStringList() << "*rc")) { + Q_FOREACH(const QString & name, dir.entryList(QStringList() << QStringLiteral("*rc"))) { HistoryItem* item = HistoryItem::load(dir.filePath(name)); if (!item) { continue; } QUrl itemUrl = item->url(); if (UrlUtils::urlIsFastLocalFile(itemUrl)) { if (!QFile::exists(itemUrl.path())) { qDebug() << "Removing" << itemUrl.path() << "from recent folders. It does not exist anymore"; item->unlink(); delete item; continue; } } HistoryItem* existingItem = mHistoryItemForUrl.value(item->url()); if (existingItem) { // We already know this url(!) update existing item dateTime // and get rid of duplicate if (existingItem->dateTime() < item->dateTime()) { existingItem->setDateTime(item->dateTime()); } item->unlink(); delete item; } else { mHistoryItemForUrl.insert(item->url(), item); q->appendRow(item); } } q->sort(0); } void garbageCollect() { while (q->rowCount() > mMaxCount) { HistoryItem* item = static_cast(q->takeRow(q->rowCount() - 1).at(0)); mHistoryItemForUrl.remove(item->url()); item->unlink(); delete item; } } }; HistoryModel::HistoryModel(QObject* parent, const QString& storageDir, int maxCount) : QStandardItemModel(parent) , d(new HistoryModelPrivate) { d->q = this; d->mStorageDir = storageDir; d->mMaxCount = maxCount; d->load(); } HistoryModel::~HistoryModel() { delete d; } void HistoryModel::addUrl(const QUrl &url, const QDateTime& _dateTime) { QDateTime dateTime = _dateTime.isValid() ? _dateTime : QDateTime::currentDateTime(); HistoryItem* historyItem = d->mHistoryItemForUrl.value(url); if (historyItem) { historyItem->setDateTime(dateTime); sort(0); } else { historyItem = HistoryItem::create(url, dateTime, d->mStorageDir); if (!historyItem) { qCritical() << "Could not save history for url" << url; return; } d->mHistoryItemForUrl.insert(url, historyItem); appendRow(historyItem); sort(0); d->garbageCollect(); } } bool HistoryModel::removeRows(int start, int count, const QModelIndex& parent) { Q_ASSERT(!parent.isValid()); for (int row = start + count - 1; row >= start ; --row) { HistoryItem* historyItem = static_cast(item(row, 0)); Q_ASSERT(historyItem); d->mHistoryItemForUrl.remove(historyItem->url()); historyItem->unlink(); } return QStandardItemModel::removeRows(start, count, parent); } } // namespace diff --git a/lib/historymodel.h b/lib/historymodel.h index c90a05ad..6aa2357f 100644 --- a/lib/historymodel.h +++ b/lib/historymodel.h @@ -1,61 +1,61 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2009 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, Cambridge, MA 02110-1301, USA. */ #ifndef HISTORYMODEL_H #define HISTORYMODEL_H // Qt #include #include // KDE // Local #include class QUrl; namespace Gwenview { struct HistoryModelPrivate; /** * A model which maintains a list of urls in the dir specified by the * storageDir parameter of its ctor. * Each url is stored in a separate KConfig file to avoid concurrency issues. */ class GWENVIEWLIB_EXPORT HistoryModel : public QStandardItemModel { Q_OBJECT public: HistoryModel(QObject* parent, const QString& storageDir, int maxCount = 20); - ~HistoryModel() Q_DECL_OVERRIDE; + ~HistoryModel() override; void addUrl(const QUrl&, const QDateTime& dateTime = QDateTime()); - bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()) Q_DECL_OVERRIDE; + bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()) override; private: HistoryModelPrivate* const d; }; } // namespace #endif /* HISTORYMODEL_H */ diff --git a/lib/hud/hudbutton.h b/lib/hud/hudbutton.h index 14ada13c..0cfefd88 100644 --- a/lib/hud/hudbutton.h +++ b/lib/hud/hudbutton.h @@ -1,70 +1,70 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2011 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, Cambridge, MA 02110-1301, USA. */ #ifndef HUDBUTTON_H #define HUDBUTTON_H // Local // KDE // Qt #include class QIcon; namespace Gwenview { struct HudButtonPrivate; /** * */ class HudButton : public QGraphicsWidget { Q_OBJECT public: explicit HudButton(QGraphicsItem* parent = nullptr); - ~HudButton() Q_DECL_OVERRIDE; + ~HudButton() override; void setIcon(const QIcon&); void setText(const QString&); - void paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*) Q_DECL_OVERRIDE; + void paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*) override; void setDefaultAction(QAction*); Q_SIGNALS: void clicked(); protected: - bool event(QEvent* event) Q_DECL_OVERRIDE; - QSizeF sizeHint(Qt::SizeHint which, const QSizeF& constraint = QSizeF()) const Q_DECL_OVERRIDE; - void mousePressEvent(QGraphicsSceneMouseEvent* event) Q_DECL_OVERRIDE; - void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) Q_DECL_OVERRIDE; + bool event(QEvent* event) override; + QSizeF sizeHint(Qt::SizeHint which, const QSizeF& constraint = QSizeF()) const override; + void mousePressEvent(QGraphicsSceneMouseEvent* event) override; + void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) override; private: HudButtonPrivate* const d; }; } // namespace #endif /* HUDBUTTON_H */ diff --git a/lib/hud/hudbuttonbox.h b/lib/hud/hudbuttonbox.h index 1dbc2aff..49a34238 100644 --- a/lib/hud/hudbuttonbox.h +++ b/lib/hud/hudbuttonbox.h @@ -1,67 +1,67 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2014 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, see . */ #ifndef HUDBUTTONBOX_H #define HUDBUTTONBOX_H #include // Local #include // KDE // Qt class QAction; class QGraphicsWidget; namespace Gwenview { class HudButton; struct HudButtonBoxPrivate; /** * A hud widget which shows a list of buttons */ class GWENVIEWLIB_EXPORT HudButtonBox : public HudWidget { Q_OBJECT public: HudButtonBox(QGraphicsWidget* parent = nullptr); - ~HudButtonBox() Q_DECL_OVERRIDE; + ~HudButtonBox() override; void setText(const QString& text); HudButton* addButton(const QString& text); HudButton* addAction(QAction* action, const QString& overrideText = QString()); void addCountDown(qreal ms); protected: - void showEvent(QShowEvent* event) Q_DECL_OVERRIDE; + void showEvent(QShowEvent* event) override; private: HudButtonBoxPrivate* const d; }; } // namespace #endif /* HUDBUTTONBOX_H */ diff --git a/lib/hud/hudcountdown.h b/lib/hud/hudcountdown.h index 4ed581ef..1aa0b562 100644 --- a/lib/hud/hudcountdown.h +++ b/lib/hud/hudcountdown.h @@ -1,60 +1,60 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2014 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, see . */ #ifndef HUDCOUNTDOWN_H #define HUDCOUNTDOWN_H // Local // KDE // Qt #include namespace Gwenview { struct HudCountDownPrivate; /** * Displays a count-down pie-chart */ class HudCountDown : public QGraphicsWidget { Q_OBJECT public: explicit HudCountDown(QGraphicsWidget* parent = nullptr); - ~HudCountDown() Q_DECL_OVERRIDE; + ~HudCountDown() override; void start(qreal ms); - void paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*) Q_DECL_OVERRIDE; + void paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*) override; Q_SIGNALS: void timeout(); private Q_SLOTS: void doUpdate(); private: HudCountDownPrivate* const d; }; } // namespace #endif /* HUDCOUNTDOWN_H */ diff --git a/lib/hud/hudlabel.h b/lib/hud/hudlabel.h index aae89230..1dff60c8 100644 --- a/lib/hud/hudlabel.h +++ b/lib/hud/hudlabel.h @@ -1,57 +1,57 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2011 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, Cambridge, MA 02110-1301, USA. */ #ifndef HUDLABEL_H #define HUDLABEL_H #include // Local // KDE // Qt #include namespace Gwenview { struct HudLabelPrivate; /** * */ class GWENVIEWLIB_EXPORT HudLabel : public QGraphicsWidget { Q_OBJECT public: explicit HudLabel(QGraphicsItem* parent = nullptr); - ~HudLabel() Q_DECL_OVERRIDE; + ~HudLabel() override; void setText(const QString&); - void paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*) Q_DECL_OVERRIDE; + void paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*) override; private: HudLabelPrivate* const d; }; } // namespace #endif /* HUDLABEL_H */ diff --git a/lib/hud/hudslider.h b/lib/hud/hudslider.h index 7d2eeb40..c57c4382 100644 --- a/lib/hud/hudslider.h +++ b/lib/hud/hudslider.h @@ -1,82 +1,82 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2011 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, Cambridge, MA 02110-1301, USA. */ #ifndef HUDSLIDER_H #define HUDSLIDER_H // Local // KDE // Qt #include #include namespace Gwenview { struct HudSliderPrivate; /** * A QGraphicsView slider. * Provides more-or-less the same API as QSlider. */ class HudSlider : public QGraphicsWidget { Q_OBJECT public: explicit HudSlider(QGraphicsItem* parent = nullptr); - ~HudSlider() Q_DECL_OVERRIDE; + ~HudSlider() override; void setRange(int min, int max); void setPageStep(int step); void setSingleStep(int step); void setValue(int value); int sliderPosition() const; void setSliderPosition(int); bool isSliderDown() const; - void paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*) Q_DECL_OVERRIDE; + void paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*) override; void triggerAction(QAbstractSlider::SliderAction); Q_SIGNALS: void valueChanged(int); void actionTriggered(int); protected: - void mousePressEvent(QGraphicsSceneMouseEvent* event) Q_DECL_OVERRIDE; - void mouseMoveEvent(QGraphicsSceneMouseEvent* event) Q_DECL_OVERRIDE; - void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) Q_DECL_OVERRIDE; - void wheelEvent(QGraphicsSceneWheelEvent* event) Q_DECL_OVERRIDE; - void keyPressEvent(QKeyEvent* event) Q_DECL_OVERRIDE; - void keyReleaseEvent(QKeyEvent* event) Q_DECL_OVERRIDE; + void mousePressEvent(QGraphicsSceneMouseEvent* event) override; + void mouseMoveEvent(QGraphicsSceneMouseEvent* event) override; + void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) override; + void wheelEvent(QGraphicsSceneWheelEvent* event) override; + void keyPressEvent(QKeyEvent* event) override; + void keyReleaseEvent(QKeyEvent* event) override; private Q_SLOTS: void doRepeatAction(int time = 50); private: HudSliderPrivate* const d; }; } // namespace #endif /* HUDSLIDER_H */ diff --git a/lib/hud/hudwidget.cpp b/lib/hud/hudwidget.cpp index f29d86a6..9c464787 100644 --- a/lib/hud/hudwidget.cpp +++ b/lib/hud/hudwidget.cpp @@ -1,157 +1,157 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 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, Cambridge, MA 02110-1301, USA. */ // Self #include "hud/hudwidget.h" // Qt #include #include #include #include #include #include // KDE #include // Local #include #include namespace Gwenview { struct HudWidgetPrivate { HudWidget* q; QPropertyAnimation* mAnim; QGraphicsWidget* mMainWidget; HudButton* mCloseButton; bool mAutoDeleteOnFadeout; void fadeTo(qreal value) { if (qFuzzyCompare(q->opacity(), value)) { return; } mAnim->stop(); mAnim->setStartValue(q->opacity()); mAnim->setEndValue(value); mAnim->start(); } }; HudWidget::HudWidget(QGraphicsWidget* parent) : QGraphicsWidget(parent) , d(new HudWidgetPrivate) { d->q = this; d->mAnim = new QPropertyAnimation(this, "opacity", this); - d->mMainWidget = 0; - d->mCloseButton = 0; + d->mMainWidget = nullptr; + d->mCloseButton = nullptr; d->mAutoDeleteOnFadeout = false; connect(d->mAnim, &QPropertyAnimation::finished, this, &HudWidget::slotFadeAnimationFinished); } HudWidget::~HudWidget() { delete d; } void HudWidget::init(QWidget* mainWidget, Options options) { QGraphicsProxyWidget* proxy = new QGraphicsProxyWidget(this); proxy->setWidget(mainWidget); init(proxy, options); } void HudWidget::init(QGraphicsWidget* mainWidget, Options options) { if (options & OptionOpaque) { setProperty("opaque", QVariant(true)); } QGraphicsLinearLayout* layout = new QGraphicsLinearLayout(this); layout->setContentsMargins(4, 4, 4, 4); d->mMainWidget = mainWidget; if (d->mMainWidget) { if (d->mMainWidget->layout()) { d->mMainWidget->layout()->setContentsMargins(0, 0, 0, 0); } layout->addItem(d->mMainWidget); } if (options & OptionCloseButton) { d->mCloseButton = new HudButton(this); - d->mCloseButton->setIcon(QIcon::fromTheme("window-close")); + d->mCloseButton->setIcon(QIcon::fromTheme(QStringLiteral("window-close"))); d->mCloseButton->setToolTip(i18nc("@info:tooltip", "Close")); layout->addItem(d->mCloseButton); layout->setAlignment(d->mCloseButton, Qt::AlignTop | Qt::AlignHCenter); connect(d->mCloseButton, &HudButton::clicked, this, &HudWidget::slotCloseButtonClicked); } } void HudWidget::slotCloseButtonClicked() { close(); closed(); } void HudWidget::paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*) { HudTheme::RenderInfo renderInfo = HudTheme::renderInfo(HudTheme::FrameWidget); painter->setPen(renderInfo.borderPen); painter->setRenderHint(QPainter::Antialiasing); painter->setBrush(renderInfo.bgBrush); painter->drawRoundedRect(boundingRect().adjusted(.5, .5, -.5, -.5), renderInfo.borderRadius, renderInfo.borderRadius); } void HudWidget::fadeIn() { d->fadeTo(1.); } void HudWidget::fadeOut() { d->fadeTo(0.); } void HudWidget::slotFadeAnimationFinished() { if (qFuzzyCompare(opacity(), 1)) { fadedIn(); } else { fadedOut(); if (d->mAutoDeleteOnFadeout) { deleteLater(); } } } void HudWidget::setAutoDeleteOnFadeout(bool value) { d->mAutoDeleteOnFadeout = value; } } // namespace diff --git a/lib/hud/hudwidget.h b/lib/hud/hudwidget.h index 91ccf95d..aa845d17 100644 --- a/lib/hud/hudwidget.h +++ b/lib/hud/hudwidget.h @@ -1,80 +1,80 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 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, Cambridge, MA 02110-1301, USA. */ #ifndef HUDWIDGET_H #define HUDWIDGET_H #include // Qt #include #include // KDE // Local namespace Gwenview { struct HudWidgetPrivate; class GWENVIEWLIB_EXPORT HudWidget : public QGraphicsWidget { Q_OBJECT public: enum Option { OptionNone = 0, OptionCloseButton = 1 << 1, OptionOpaque = 1 << 2 }; Q_DECLARE_FLAGS(Options, Option) explicit HudWidget(QGraphicsWidget* parent = nullptr); - ~HudWidget() Q_DECL_OVERRIDE; + ~HudWidget() override; void init(QWidget*, Options options); void init(QGraphicsWidget*, Options options); void setAutoDeleteOnFadeout(bool autoDelete); - void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) Q_DECL_OVERRIDE; + void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override; public Q_SLOTS: void fadeIn(); void fadeOut(); Q_SIGNALS: void closed(); void fadedIn(); void fadedOut(); private Q_SLOTS: void slotCloseButtonClicked(); void slotFadeAnimationFinished(); private: HudWidgetPrivate* const d; }; } // namespace Q_DECLARE_OPERATORS_FOR_FLAGS(Gwenview::HudWidget::Options) #endif /* HUDWIDGET_H */ diff --git a/lib/imageformats/fitshandler.h b/lib/imageformats/fitshandler.h index 8c8c898e..31cd2bcc 100644 --- a/lib/imageformats/fitshandler.h +++ b/lib/imageformats/fitshandler.h @@ -1,42 +1,42 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 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, Cambridge, MA 02110-1301, USA. */ #pragma once #include namespace Gwenview { /** * A FITS handler. */ class FitsHandler : public QImageIOHandler { public: - bool canRead() const Q_DECL_OVERRIDE; - bool read(QImage *image) Q_DECL_OVERRIDE; + bool canRead() const override; + bool read(QImage *image) override; - bool supportsOption(ImageOption option) const Q_DECL_OVERRIDE; - QVariant option(ImageOption option) const Q_DECL_OVERRIDE; + bool supportsOption(ImageOption option) const override; + QVariant option(ImageOption option) const override; }; } // namespace diff --git a/lib/imageformats/fitsplugin.h b/lib/imageformats/fitsplugin.h index 38b505e4..c8f6c664 100644 --- a/lib/imageformats/fitsplugin.h +++ b/lib/imageformats/fitsplugin.h @@ -1,47 +1,47 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 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, Cambridge, MA 02110-1301, USA. */ #pragma once #define QT_STATICPLUGIN #include #include class QImageIOHandler; class QIODevice; class QStringList; extern const QStaticPlugin qt_static_plugin_FitsPlugin(); class Q_CORE_EXPORT FitsPlugin : public QImageIOPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QImageIOHandlerFactoryInterface" FILE "fitsplugin.json") public: - ~FitsPlugin() Q_DECL_OVERRIDE { } + ~FitsPlugin() override { } QStringList keys() const; - Capabilities capabilities(QIODevice *device, const QByteArray &format) const Q_DECL_OVERRIDE; - QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const Q_DECL_OVERRIDE; + Capabilities capabilities(QIODevice *device, const QByteArray &format) const override; + QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const override; }; diff --git a/lib/imagemetainfomodel.cpp b/lib/imagemetainfomodel.cpp index e659fdd7..fd30866c 100644 --- a/lib/imagemetainfomodel.cpp +++ b/lib/imagemetainfomodel.cpp @@ -1,577 +1,577 @@ // 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 #include #include // Local #ifdef HAVE_FITS #include "imageformats/fitsformat/fitsdata.h" #include "urlutils.h" #endif namespace Gwenview { enum GroupRow { NoGroupSpace = -2, NoGroup = -1, GeneralGroup, ExifGroup, #ifdef HAVE_FITS FitsGroup, #endif IptcGroup, XmpGroup }; 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 += '\n'; + 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("General.Name", i18nc("@item:intable Image file name", "Name"), QString()); - group->addEntry("General.Size", i18nc("@item:intable", "File Size"), QString()); - group->addEntry("General.Time", i18nc("@item:intable", "File Time"), QString()); - group->addEntry("General.ImageSize", i18nc("@item:intable", "Image Size"), QString()); - group->addEntry("General.Comment", i18nc("@item:intable", "Comment"), QString()); + 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); Q_FOREACH(MetaInfoGroup::Entry * entry, 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("EXIF"); + d->mMetaInfoGroupVector[ExifGroup] = new MetaInfoGroup(QStringLiteral("EXIF")); #ifdef HAVE_FITS - d->mMetaInfoGroupVector[FitsGroup] = new MetaInfoGroup("FITS"); + d->mMetaInfoGroupVector[FitsGroup] = new MetaInfoGroup(QStringLiteral("FITS")); #endif - d->mMetaInfoGroupVector[IptcGroup] = new MetaInfoGroup("IPTC"); - d->mMetaInfoGroupVector[XmpGroup] = new MetaInfoGroup("XMP"); + 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, "General.Name", item.name()); - d->setGroupEntryValue(GeneralGroup, "General.Size", sizeString); - d->setGroupEntryValue(GeneralGroup, "General.Time", timeString); + 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(".fit", Qt::CaseInsensitive) || - url.fileName().endsWith(".fits", Qt::CaseInsensitive))) { + if (UrlUtils::urlIsFastLocalFile(url) && (url.fileName().endsWith(QStringLiteral(".fit"), Qt::CaseInsensitive) || + url.fileName().endsWith(QStringLiteral(".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("=")) { - key = record.section(' ', 0, 0).simplified(); + if (!record.contains(QStringLiteral("="))) { + key = record.section(QLatin1Char(' '), 0, 0).simplified(); keyStr = key; - value = record.section(' ', 1, -1).simplified(); + value = record.section(QLatin1Char(' '), 1, -1).simplified(); } else { - key = record.section('=', 0, 0).simplified(); - if (record.contains('/')) { - keyStr = record.section('/', -1, -1).simplified(); - value = record.section('=', 1, -1).section('/', 0, 0); + 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('=', 1, -1); + value = record.section(QLatin1Char('='), 1, -1); } - value.remove("\'"); + 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("Fits."+key, keyStr, value); + 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 += ' '; + imageSize += QLatin1Char(' '); imageSize += i18nc( "@item:intable %1 is number of millions of pixels in image", "(%1MP)", megaPixelsString); } } else { - imageSize = '-'; + imageSize = QLatin1Char('-'); } - d->setGroupEntryValue(GeneralGroup, "General.ImageSize", imageSize); + 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, "General.Comment", QString::fromUtf8(image->comment().c_str())); + 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/imagemetainfomodel.h b/lib/imagemetainfomodel.h index ec6ee05d..09605c99 100644 --- a/lib/imagemetainfomodel.h +++ b/lib/imagemetainfomodel.h @@ -1,73 +1,73 @@ // 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. */ #ifndef IMAGEMETAINFO_H #define IMAGEMETAINFO_H #include // Qt #include // KDE // Local class QUrl; namespace Exiv2 { class Image; } namespace Gwenview { struct ImageMetaInfoModelPrivate; class GWENVIEWLIB_EXPORT ImageMetaInfoModel : public QAbstractItemModel { Q_OBJECT public: ImageMetaInfoModel(); - ~ImageMetaInfoModel() Q_DECL_OVERRIDE; + ~ImageMetaInfoModel() override; void setUrl(const QUrl&); void setImageSize(const QSize&); void setExiv2Image(const Exiv2::Image*); QString keyForIndex(const QModelIndex&) const; void getInfoForKey(const QString& key, QString* label, QString* value) const; QString getValueForKey(const QString& key) const; - QModelIndex index(int row, int col, const QModelIndex& parent = QModelIndex()) const Q_DECL_OVERRIDE; - QModelIndex parent(const QModelIndex&) const Q_DECL_OVERRIDE; - int rowCount(const QModelIndex& = QModelIndex()) const Q_DECL_OVERRIDE; - int columnCount(const QModelIndex& = QModelIndex()) const Q_DECL_OVERRIDE; - QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; - QVariant data(const QModelIndex&, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; + QModelIndex index(int row, int col, const QModelIndex& parent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex&) const override; + int rowCount(const QModelIndex& = QModelIndex()) const override; + int columnCount(const QModelIndex& = QModelIndex()) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + QVariant data(const QModelIndex&, int role = Qt::DisplayRole) const override; private: ImageMetaInfoModelPrivate* const d; friend struct ImageMetaInfoModelPrivate; }; } // namespace #endif /* IMAGEMETAINFO_H */ diff --git a/lib/invisiblebuttongroup.cpp b/lib/invisiblebuttongroup.cpp index e9fd7449..a06569a4 100644 --- a/lib/invisiblebuttongroup.cpp +++ b/lib/invisiblebuttongroup.cpp @@ -1,79 +1,79 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 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, Cambridge, MA 02110-1301, USA. */ // Self #include "invisiblebuttongroup.h" // Qt #include #include // KDE #include // Local namespace Gwenview { struct InvisibleButtonGroupPrivate { QButtonGroup* mGroup; }; InvisibleButtonGroup::InvisibleButtonGroup(QWidget* parent) : QWidget(parent) , d(new InvisibleButtonGroupPrivate) { hide(); d->mGroup = new QButtonGroup(this); d->mGroup->setExclusive(true); connect(d->mGroup, static_cast(&QButtonGroup::buttonClicked), this, &InvisibleButtonGroup::selectionChanged); - const QString name = metaObject()->className(); + const QString name = QString::fromLatin1(metaObject()->className()); if (!KConfigDialogManager::propertyMap()->contains(name)) { KConfigDialogManager::propertyMap()->insert(name, "current"); KConfigDialogManager::changedMap()->insert(name, SIGNAL(selectionChanged(int))); } } InvisibleButtonGroup::~InvisibleButtonGroup() { delete d; } int InvisibleButtonGroup::selected() const { return d->mGroup->checkedId(); } void InvisibleButtonGroup::addButton(QAbstractButton* button, int id) { d->mGroup->addButton(button, id); } void InvisibleButtonGroup::setSelected(int id) { QAbstractButton* button = d->mGroup->button(id); if (button) { button->setChecked(true); } } } // namespace diff --git a/lib/jpegcontent.cpp b/lib/jpegcontent.cpp index d203da5a..bb810dd4 100644 --- a/lib/jpegcontent.cpp +++ b/lib/jpegcontent.cpp @@ -1,644 +1,644 @@ // 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. */ #include "jpegcontent.h" // System #include #include #include #include extern "C" { #include #include "transupp.h" } // Qt #include #include #include #include #include #include // KDE #include // Exiv2 #include #include // Local #include "jpegerrormanager.h" #include "iodevicejpegsourcemanager.h" #include "exiv2imageloader.h" #include "gwenviewconfig.h" namespace Gwenview { const int INMEM_DST_DELTA = 4096; //----------------------------------------------- // // In-memory data destination manager for libjpeg // //----------------------------------------------- struct inmem_dest_mgr : public jpeg_destination_mgr { QByteArray* mOutput; void dump() { qDebug() << "dest_mgr:\n"; qDebug() << "- next_output_byte: " << next_output_byte; qDebug() << "- free_in_buffer: " << free_in_buffer; qDebug() << "- output size: " << mOutput->size(); } }; void inmem_init_destination(j_compress_ptr cinfo) { inmem_dest_mgr* dest = (inmem_dest_mgr*)(cinfo->dest); if (dest->mOutput->size() == 0) { dest->mOutput->resize(INMEM_DST_DELTA); } dest->free_in_buffer = dest->mOutput->size(); dest->next_output_byte = (JOCTET*)(dest->mOutput->data()); } boolean inmem_empty_output_buffer(j_compress_ptr cinfo) { inmem_dest_mgr* dest = (inmem_dest_mgr*)(cinfo->dest); dest->mOutput->resize(dest->mOutput->size() + INMEM_DST_DELTA); dest->next_output_byte = (JOCTET*)(dest->mOutput->data() + dest->mOutput->size() - INMEM_DST_DELTA); dest->free_in_buffer = INMEM_DST_DELTA; return true; } void inmem_term_destination(j_compress_ptr cinfo) { inmem_dest_mgr* dest = (inmem_dest_mgr*)(cinfo->dest); int finalSize = dest->next_output_byte - (JOCTET*)(dest->mOutput->data()); Q_ASSERT(finalSize >= 0); dest->mOutput->resize(finalSize); } //--------------------- // // JpegContent::Private // //--------------------- struct JpegContent::Private { // JpegContent usually stores the image pixels as compressed JPEG data in // mRawData. However if the image is set with setImage() because the user // performed a lossy image manipulation, mRawData is cleared and the image // pixels are kept in mImage until updateRawDataFromImage() is called. QImage mImage; QByteArray mRawData; QSize mSize; QString mComment; bool mPendingTransformation; QMatrix mTransformMatrix; Exiv2::ExifData mExifData; QString mErrorString; Private() { mPendingTransformation = false; } void setupInmemDestination(j_compress_ptr cinfo, QByteArray* outputData) { Q_ASSERT(!cinfo->dest); inmem_dest_mgr* dest = (inmem_dest_mgr*) (*cinfo->mem->alloc_small)((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(inmem_dest_mgr)); cinfo->dest = (struct jpeg_destination_mgr*)(dest); dest->init_destination = inmem_init_destination; dest->empty_output_buffer = inmem_empty_output_buffer; dest->term_destination = inmem_term_destination; dest->mOutput = outputData; } bool readSize() { struct jpeg_decompress_struct srcinfo; // Init JPEG structs JPEGErrorManager errorManager; // Initialize the JPEG decompression object srcinfo.err = &errorManager; jpeg_create_decompress(&srcinfo); if (setjmp(errorManager.jmp_buffer)) { qCritical() << "libjpeg fatal error\n"; return false; } // Specify data source for decompression QBuffer buffer(&mRawData); buffer.open(QIODevice::ReadOnly); IODeviceJpegSourceManager::setup(&srcinfo, &buffer); // Read the header jcopy_markers_setup(&srcinfo, JCOPYOPT_ALL); int result = jpeg_read_header(&srcinfo, true); if (result != JPEG_HEADER_OK) { qCritical() << "Could not read jpeg header\n"; jpeg_destroy_decompress(&srcinfo); return false; } mSize = QSize(srcinfo.image_width, srcinfo.image_height); jpeg_destroy_decompress(&srcinfo); return true; } bool updateRawDataFromImage() { QBuffer buffer; QImageWriter writer(&buffer, "jpeg"); if (!writer.write(mImage)) { mErrorString = writer.errorString(); return false; } mRawData = buffer.data(); mImage = QImage(); return true; } }; //------------ // // JpegContent // //------------ JpegContent::JpegContent() { d = new JpegContent::Private(); } JpegContent::~JpegContent() { delete d; } bool JpegContent::load(const QString& path) { QFile file(path); if (!file.open(QIODevice::ReadOnly)) { qCritical() << "Could not open '" << path << "' for reading\n"; return false; } return loadFromData(file.readAll()); } bool JpegContent::loadFromData(const QByteArray& data) { Exiv2::Image::AutoPtr image; Exiv2ImageLoader loader; if (!loader.load(data)) { qCritical() << "Could not load image with Exiv2, reported error:" << loader.errorMessage(); } image = loader.popImage(); return loadFromData(data, image.get()); } bool JpegContent::loadFromData(const QByteArray& data, Exiv2::Image* exiv2Image) { d->mPendingTransformation = false; d->mTransformMatrix.reset(); d->mRawData = data; if (d->mRawData.size() == 0) { qCritical() << "No data\n"; return false; } if (!d->readSize()) return false; d->mExifData = exiv2Image->exifData(); d->mComment = QString::fromUtf8(exiv2Image->comment().c_str()); if (!GwenviewConfig::applyExifOrientation()) { return true; } // Adjust the size according to the orientation switch (orientation()) { case TRANSPOSE: case ROT_90: case TRANSVERSE: case ROT_270: d->mSize.transpose(); break; default: break; } return true; } QByteArray JpegContent::rawData() const { return d->mRawData; } Orientation JpegContent::orientation() const { Exiv2::ExifKey key("Exif.Image.Orientation"); Exiv2::ExifData::iterator it = d->mExifData.findKey(key); // We do the same checks as in libexiv2's src/crwimage.cpp: // http://dev.exiv2.org/projects/exiv2/repository/entry/trunk/src/crwimage.cpp?rev=2681#L1336 if (it == d->mExifData.end() || it->count() == 0 || it->typeId() != Exiv2::unsignedShort) { return NOT_AVAILABLE; } return Orientation(it->toLong()); } int JpegContent::dotsPerMeterX() const { - return dotsPerMeter("XResolution"); + return dotsPerMeter(QStringLiteral("XResolution")); } int JpegContent::dotsPerMeterY() const { - return dotsPerMeter("YResolution"); + return dotsPerMeter(QStringLiteral("YResolution")); } int JpegContent::dotsPerMeter(const QString& keyName) const { Exiv2::ExifKey keyResUnit("Exif.Image.ResolutionUnit"); Exiv2::ExifData::iterator it = d->mExifData.findKey(keyResUnit); if (it == d->mExifData.end()) { return 0; } int res = it->toLong(); - QString keyVal = "Exif.Image." + keyName; + QString keyVal = QStringLiteral("Exif.Image.") + keyName; Exiv2::ExifKey keyResolution(keyVal.toLocal8Bit().data()); it = d->mExifData.findKey(keyResolution); if (it == d->mExifData.end()) { return 0; } // The unit for measuring XResolution and YResolution. The same unit is used for both XResolution and YResolution. // If the image resolution in unknown, 2 (inches) is designated. // Default = 2 // 2 = inches // 3 = centimeters // Other = reserved const float INCHESPERMETER = (100. / 2.54); switch (res) { case 3: // dots per cm return int(it->toLong() * 100); default: // dots per inch return int(it->toLong() * INCHESPERMETER); } return 0; } void JpegContent::resetOrientation() { Exiv2::ExifKey key("Exif.Image.Orientation"); Exiv2::ExifData::iterator it = d->mExifData.findKey(key); if (it == d->mExifData.end()) { return; } *it = uint16_t(NORMAL); } QSize JpegContent::size() const { return d->mSize; } QString JpegContent::comment() const { return d->mComment; } void JpegContent::setComment(const QString& comment) { d->mComment = comment; } static QMatrix createRotMatrix(int angle) { QMatrix matrix; matrix.rotate(angle); return matrix; } static QMatrix createScaleMatrix(int dx, int dy) { QMatrix matrix; matrix.scale(dx, dy); return matrix; } struct OrientationInfo { OrientationInfo() : orientation(NOT_AVAILABLE) , jxform(JXFORM_NONE) {} OrientationInfo(Orientation o, QMatrix m, JXFORM_CODE j) : orientation(o), matrix(m), jxform(j) {} Orientation orientation; QMatrix matrix; JXFORM_CODE jxform; }; typedef QList OrientationInfoList; static const OrientationInfoList& orientationInfoList() { static OrientationInfoList list; if (list.size() == 0) { QMatrix rot90 = createRotMatrix(90); QMatrix hflip = createScaleMatrix(-1, 1); QMatrix vflip = createScaleMatrix(1, -1); list << OrientationInfo() << OrientationInfo(NORMAL, QMatrix(), JXFORM_NONE) << OrientationInfo(HFLIP, hflip, JXFORM_FLIP_H) << OrientationInfo(ROT_180, createRotMatrix(180), JXFORM_ROT_180) << OrientationInfo(VFLIP, vflip, JXFORM_FLIP_V) << OrientationInfo(TRANSPOSE, hflip * rot90, JXFORM_TRANSPOSE) << OrientationInfo(ROT_90, rot90, JXFORM_ROT_90) << OrientationInfo(TRANSVERSE, vflip * rot90, JXFORM_TRANSVERSE) << OrientationInfo(ROT_270, createRotMatrix(270), JXFORM_ROT_270) ; } return list; } void JpegContent::transform(Orientation orientation) { if (orientation != NOT_AVAILABLE && orientation != NORMAL) { d->mPendingTransformation = true; OrientationInfoList::ConstIterator it(orientationInfoList().begin()), end(orientationInfoList().end()); for (; it != end; ++it) { if ((*it).orientation == orientation) { d->mTransformMatrix = (*it).matrix * d->mTransformMatrix; break; } } if (it == end) { qWarning() << "Could not find matrix for orientation\n"; } } } #if 0 static void dumpMatrix(const QMatrix& matrix) { qDebug() << "matrix | " << matrix.m11() << ", " << matrix.m12() << " |\n"; qDebug() << " | " << matrix.m21() << ", " << matrix.m22() << " |\n"; qDebug() << " ( " << matrix.dx() << ", " << matrix.dy() << " )\n"; } #endif static bool matricesAreSame(const QMatrix& m1, const QMatrix& m2, double tolerance) { return fabs(m1.m11() - m2.m11()) < tolerance && fabs(m1.m12() - m2.m12()) < tolerance && fabs(m1.m21() - m2.m21()) < tolerance && fabs(m1.m22() - m2.m22()) < tolerance && fabs(m1.dx() - m2.dx()) < tolerance && fabs(m1.dy() - m2.dy()) < tolerance; } static JXFORM_CODE findJxform(const QMatrix& matrix) { OrientationInfoList::ConstIterator it(orientationInfoList().begin()), end(orientationInfoList().end()); for (; it != end; ++it) { if (matricesAreSame((*it).matrix, matrix, 0.001)) { return (*it).jxform; } } qWarning() << "findJxform: failed\n"; return JXFORM_NONE; } void JpegContent::applyPendingTransformation() { if (d->mRawData.size() == 0) { qCritical() << "No data loaded\n"; return; } // The following code is inspired by jpegtran.c from the libjpeg // Init JPEG structs struct jpeg_decompress_struct srcinfo; struct jpeg_compress_struct dstinfo; jvirt_barray_ptr * src_coef_arrays; jvirt_barray_ptr * dst_coef_arrays; // Initialize the JPEG decompression object JPEGErrorManager srcErrorManager; srcinfo.err = &srcErrorManager; jpeg_create_decompress(&srcinfo); if (setjmp(srcErrorManager.jmp_buffer)) { qCritical() << "libjpeg error in src\n"; return; } // Initialize the JPEG compression object JPEGErrorManager dstErrorManager; dstinfo.err = &dstErrorManager; jpeg_create_compress(&dstinfo); if (setjmp(dstErrorManager.jmp_buffer)) { qCritical() << "libjpeg error in dst\n"; return; } // Specify data source for decompression QBuffer buffer(&d->mRawData); buffer.open(QIODevice::ReadOnly); IODeviceJpegSourceManager::setup(&srcinfo, &buffer); // Enable saving of extra markers that we want to copy jcopy_markers_setup(&srcinfo, JCOPYOPT_ALL); (void) jpeg_read_header(&srcinfo, true); // Init transformation jpeg_transform_info transformoption; memset(&transformoption, 0, sizeof(jpeg_transform_info)); transformoption.transform = findJxform(d->mTransformMatrix); jtransform_request_workspace(&srcinfo, &transformoption); /* Read source file as DCT coefficients */ src_coef_arrays = jpeg_read_coefficients(&srcinfo); /* Initialize destination compression parameters from source values */ jpeg_copy_critical_parameters(&srcinfo, &dstinfo); /* Adjust destination parameters if required by transform options; * also find out which set of coefficient arrays will hold the output. */ dst_coef_arrays = jtransform_adjust_parameters(&srcinfo, &dstinfo, src_coef_arrays, &transformoption); /* Specify data destination for compression */ QByteArray output; output.resize(d->mRawData.size()); d->setupInmemDestination(&dstinfo, &output); /* Start compressor (note no image data is actually written here) */ jpeg_write_coefficients(&dstinfo, dst_coef_arrays); /* Copy to the output file any extra markers that we want to preserve */ jcopy_markers_execute(&srcinfo, &dstinfo, JCOPYOPT_ALL); /* Execute image transformation, if any */ jtransform_execute_transformation(&srcinfo, &dstinfo, src_coef_arrays, &transformoption); /* Finish compression and release memory */ jpeg_finish_compress(&dstinfo); jpeg_destroy_compress(&dstinfo); (void) jpeg_finish_decompress(&srcinfo); jpeg_destroy_decompress(&srcinfo); // Set rawData to our new JPEG d->mRawData = output; } QImage JpegContent::thumbnail() const { QImage image; if (!d->mExifData.empty()) { #if(EXIV2_TEST_VERSION(0,17,91)) Exiv2::ExifThumbC thumb(d->mExifData); Exiv2::DataBuf thumbnail = thumb.copy(); #else Exiv2::DataBuf thumbnail = d->mExifData.copyThumbnail(); #endif image.loadFromData(thumbnail.pData_, thumbnail.size_); } return image; } void JpegContent::setThumbnail(const QImage& thumbnail) { if (d->mExifData.empty()) { return; } QByteArray array; QBuffer buffer(&array); buffer.open(QIODevice::WriteOnly); QImageWriter writer(&buffer, "JPEG"); if (!writer.write(thumbnail)) { qCritical() << "Could not write thumbnail\n"; return; } #if (EXIV2_TEST_VERSION(0,17,91)) Exiv2::ExifThumb thumb(d->mExifData); thumb.setJpegThumbnail((unsigned char*)array.data(), array.size()); #else d->mExifData.setJpegThumbnail((unsigned char*)array.data(), array.size()); #endif } bool JpegContent::save(const QString& path) { QFile file(path); if (!file.open(QIODevice::WriteOnly)) { d->mErrorString = i18nc("@info", "Could not open file for writing."); return false; } return save(&file); } bool JpegContent::save(QIODevice* device) { if (!d->mImage.isNull()) { if (!d->updateRawDataFromImage()) { return false; } } if (d->mRawData.size() == 0) { d->mErrorString = i18nc("@info", "No data to store."); return false; } if (d->mPendingTransformation) { applyPendingTransformation(); d->mPendingTransformation = false; } Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open((unsigned char*)d->mRawData.data(), d->mRawData.size()); // Store Exif info image->setExifData(d->mExifData); image->setComment(d->mComment.toUtf8().toStdString()); image->writeMetadata(); // Update mRawData Exiv2::BasicIo& io = image->io(); d->mRawData.resize(io.size()); io.read((unsigned char*)d->mRawData.data(), io.size()); QDataStream stream(device); stream.writeRawData(d->mRawData.data(), d->mRawData.size()); // Make sure we are up to date loadFromData(d->mRawData); return true; } QString JpegContent::errorString() const { return d->mErrorString; } void JpegContent::setImage(const QImage& image) { d->mRawData.clear(); d->mImage = image; d->mSize = image.size(); d->mExifData["Exif.Photo.PixelXDimension"] = image.width(); d->mExifData["Exif.Photo.PixelYDimension"] = image.height(); resetOrientation(); d->mPendingTransformation = false; d->mTransformMatrix = QMatrix(); } } // namespace diff --git a/lib/kindproxymodel.h b/lib/kindproxymodel.h index 96e53e44..70380470 100644 --- a/lib/kindproxymodel.h +++ b/lib/kindproxymodel.h @@ -1,59 +1,59 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2012 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, Cambridge, MA 02110-1301, USA. */ #ifndef KINDPROXYMODEL_H #define KINDPROXYMODEL_H // Local #include #include // KDE // Qt #include namespace Gwenview { struct KindProxyModelPrivate; /** * A simple proxy model allowing only objects of a certain kind */ class GWENVIEWLIB_EXPORT KindProxyModel : public QSortFilterProxyModel { Q_OBJECT public: explicit KindProxyModel(QObject* parent = nullptr); - ~KindProxyModel() Q_DECL_OVERRIDE; + ~KindProxyModel() override; void setKindFilter(MimeTypeUtils::Kinds); MimeTypeUtils::Kinds kindFilter() const; protected: - bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const Q_DECL_OVERRIDE; + bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override; private: KindProxyModelPrivate* const d; }; } // namespace #endif /* KINDPROXYMODEL_H */ diff --git a/lib/memoryutils.cpp b/lib/memoryutils.cpp index 4e3aec12..df57f8ab 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( "/proc/meminfo" ); + 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( "MemTotal:" ) ) - return (cachedValue = (Q_UINT64_C(1024) * entry.section( ' ', -2, -2 ).toULongLong())); + if ( entry.startsWith( QStringLiteral("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( "/proc/meminfo" ); + 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] = { "MemFree:", "Buffers:", "Cached:", "SwapFree:", "SwapTotal:" }; + 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( ' ', -2, -2 ).toULongLong( &foundValues[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 diff --git a/lib/mimetypeutils.cpp b/lib/mimetypeutils.cpp index 2103053a..f203cafc 100644 --- a/lib/mimetypeutils.cpp +++ b/lib/mimetypeutils.cpp @@ -1,232 +1,232 @@ // vim: set tabstop=4 shiftwidth=4 expandtab /* Gwenview - A simple image viewer for KDE Copyright 2006 Aurelien Gateau 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. */ #include "mimetypeutils.h" #include "mimetypeutils_p.h" // Qt #include #include #include #include #include #include #include #include // KDE #include #include #include // Local #include #include #include namespace Gwenview { namespace MimeTypeUtils { static inline QString resolveAlias(const QString& name) { QMimeDatabase db; return db.mimeTypeForName(name).name(); } static void resolveAliasInList(QStringList* list) { QStringList::Iterator it = list->begin(), end = list->end(); for (; it != end; ++it) { *it = resolveAlias(*it); } } static void addRawMimeTypes(QStringList* list) { // need to invent more intelligent way to whitelist raws - *list += "image/x-nikon-nef"; - *list += "image/x-nikon-nrw"; - *list += "image/x-canon-cr2"; - *list += "image/x-canon-crw"; - *list += "image/x-pentax-pef"; - *list += "image/x-adobe-dng"; - *list += "image/x-sony-arw"; - *list += "image/x-minolta-mrw"; - *list += "image/x-panasonic-raw"; - *list += "image/x-panasonic-raw2"; - *list += "image/x-panasonic-rw"; - *list += "image/x-panasonic-rw2"; - *list += "image/x-samsung-srw"; - *list += "image/x-olympus-orf"; - *list += "image/x-fuji-raf"; - *list += "image/x-kodak-dcr"; - *list += "image/x-sigma-x3f"; + *list += QStringLiteral("image/x-nikon-nef"); + *list += QStringLiteral("image/x-nikon-nrw"); + *list += QStringLiteral("image/x-canon-cr2"); + *list += QStringLiteral("image/x-canon-crw"); + *list += QStringLiteral("image/x-pentax-pef"); + *list += QStringLiteral("image/x-adobe-dng"); + *list += QStringLiteral("image/x-sony-arw"); + *list += QStringLiteral("image/x-minolta-mrw"); + *list += QStringLiteral("image/x-panasonic-raw"); + *list += QStringLiteral("image/x-panasonic-raw2"); + *list += QStringLiteral("image/x-panasonic-rw"); + *list += QStringLiteral("image/x-panasonic-rw2"); + *list += QStringLiteral("image/x-samsung-srw"); + *list += QStringLiteral("image/x-olympus-orf"); + *list += QStringLiteral("image/x-fuji-raf"); + *list += QStringLiteral("image/x-kodak-dcr"); + *list += QStringLiteral("image/x-sigma-x3f"); } const QStringList& rasterImageMimeTypes() { static QStringList list; if (list.isEmpty()) { const auto supported = QImageReader::supportedMimeTypes(); for (auto mime: supported) { list << resolveAlias(QString::fromUtf8(mime)); } // We don't want svg images to be considered as raster images Q_FOREACH(const QString& mimeType, svgImageMimeTypes()) { list.removeOne(mimeType); } addRawMimeTypes(&list); } return list; } const QStringList& svgImageMimeTypes() { static QStringList list; if (list.isEmpty()) { - list << "image/svg+xml" << "image/svg+xml-compressed"; + list << QStringLiteral("image/svg+xml") << QStringLiteral("image/svg+xml-compressed"); resolveAliasInList(&list); } return list; } const QStringList& imageMimeTypes() { static QStringList list; if (list.isEmpty()) { list = rasterImageMimeTypes(); list += svgImageMimeTypes(); } return list; } QString urlMimeType(const QUrl &url) { if (url.isEmpty()) { - return "unknown"; + return QStringLiteral("unknown"); } QMimeDatabase db; return db.mimeTypeForUrl(url).name(); } Kind mimeTypeKind(const QString& mimeType) { if (rasterImageMimeTypes().contains(mimeType)) { return KIND_RASTER_IMAGE; } if (svgImageMimeTypes().contains(mimeType)) { return KIND_SVG_IMAGE; } if (mimeType.startsWith(QLatin1String("video/"))) { return KIND_VIDEO; } if (mimeType.startsWith(QLatin1String("inode/directory"))) { return KIND_DIR; } if (!ArchiveUtils::protocolForMimeType(mimeType).isEmpty()) { return KIND_ARCHIVE; } return KIND_FILE; } Kind fileItemKind(const KFileItem& item) { GV_RETURN_VALUE_IF_FAIL(!item.isNull(), KIND_UNKNOWN); return mimeTypeKind(item.mimetype()); } Kind urlKind(const QUrl &url) { return mimeTypeKind(urlMimeType(url)); } QMimeData* selectionMimeData(const KFileItemList& selectedFiles) { QMimeData* mimeData = new QMimeData; if (selectedFiles.count() == 1) { const QUrl url = selectedFiles.first().url(); const MimeTypeUtils::Kind mimeKind = MimeTypeUtils::urlKind(url); bool documentIsModified = false; if (mimeKind == MimeTypeUtils::KIND_RASTER_IMAGE || mimeKind == MimeTypeUtils::KIND_SVG_IMAGE) { const Document::Ptr doc = DocumentFactory::instance()->load(url); doc->waitUntilLoaded(); documentIsModified = doc->isModified(); QString suggestedFileName; if (mimeKind == MimeTypeUtils::KIND_RASTER_IMAGE) { mimeData->setImageData(doc->image()); // Set the filename extension to PNG, as it is the first // entry in the combobox when pasting to Dolphin suggestedFileName = QFileInfo(url.fileName()).completeBaseName() + QStringLiteral(".png"); } else { mimeData->setData(MimeTypeUtils::urlMimeType(url), doc->rawData()); suggestedFileName = url.fileName(); } mimeData->setData(QStringLiteral("application/x-kde-suggestedfilename"), QFile::encodeName(suggestedFileName)); } // Don't set the URL to support pasting edited images to // applications preferring the URL otherwise, e.g. Dolphin if (!documentIsModified) { mimeData->setUrls({url}); } } else { mimeData->setUrls(selectedFiles.urlList()); } return mimeData; } DataAccumulator::DataAccumulator(KIO::TransferJob* job) : QObject() , mFinished(false) { connect(job, SIGNAL(data(KIO::Job*,QByteArray)), SLOT(slotDataReceived(KIO::Job*,QByteArray))); connect(job, SIGNAL(result(KJob*)), SLOT(slotFinished())); } void DataAccumulator::slotDataReceived(KIO::Job*, const QByteArray& data) { mData += data; } void DataAccumulator::slotFinished() { mFinished = true; } } // namespace MimeTypeUtils } // namespace Gwenview diff --git a/lib/mpris2/mprismediaplayer2player.cpp b/lib/mpris2/mprismediaplayer2player.cpp index 530c6e73..8070cf2e 100644 --- a/lib/mpris2/mprismediaplayer2player.cpp +++ b/lib/mpris2/mprismediaplayer2player.cpp @@ -1,384 +1,384 @@ /* Gwenview: an image viewer Copyright 2018 Friedrich W. H. Kossebau 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. */ #include "mprismediaplayer2player.h" #include // lib #include #include #include #include #include #include #include // KF #include #include // Qt #include #include #include namespace Gwenview { static const double MAX_RATE = 1.0; static const double MIN_RATE = 1.0; MprisMediaPlayer2Player::MprisMediaPlayer2Player(const QString &objectDBusPath, SlideShow* slideShow, ContextManager* contextManager, QAction* toggleSlideShowAction, QAction* fullScreenAction, QAction* previousAction, QAction* nextAction, QObject* parent) : DBusAbstractAdaptor(objectDBusPath, parent) , mSlideShow(slideShow) , mContextManager(contextManager) , mToggleSlideShowAction(toggleSlideShowAction) , mFullScreenAction(fullScreenAction) , mPreviousAction(previousAction) , mNextAction(nextAction) , mSlideShowEnabled(mToggleSlideShowAction->isEnabled()) , mPreviousEnabled(mPreviousAction->isEnabled()) , mNextEnabled(mNextAction->isEnabled()) { updatePlaybackStatus(); connect(mSlideShow, &SlideShow::stateChanged, this, &MprisMediaPlayer2Player::onSlideShowStateChanged); connect(mSlideShow, &SlideShow::intervalChanged, this, &MprisMediaPlayer2Player::onMetaInfoUpdated); connect(mContextManager, &ContextManager::currentUrlChanged, this, &MprisMediaPlayer2Player::onCurrentUrlChanged); connect(mSlideShow->randomAction(), &QAction::toggled, this, &MprisMediaPlayer2Player::onRandomActionToggled); connect(mToggleSlideShowAction, &QAction::changed, this, &MprisMediaPlayer2Player::onToggleSlideShowActionChanged); connect(mFullScreenAction, &QAction::toggled, this, &MprisMediaPlayer2Player::onFullScreenActionToggled); connect(mNextAction, &QAction::changed, this, &MprisMediaPlayer2Player::onNextActionChanged); connect(mPreviousAction, &QAction::changed, this, &MprisMediaPlayer2Player::onPreviousActionChanged); } MprisMediaPlayer2Player::~MprisMediaPlayer2Player() { } bool MprisMediaPlayer2Player::updatePlaybackStatus() { const QString newStatus = (!mSlideShowEnabled || !mFullScreenAction->isChecked()) ? QStringLiteral("Stopped") : mSlideShow->isRunning() ? QStringLiteral("Playing") : /* else */ QStringLiteral("Paused"); const bool changed = (newStatus != mPlaybackStatus); if (changed) { mPlaybackStatus = newStatus; } return changed; } QString MprisMediaPlayer2Player::playbackStatus() const { return mPlaybackStatus; } bool MprisMediaPlayer2Player::canGoNext() const { return mNextEnabled; } void MprisMediaPlayer2Player::Next() { mNextAction->trigger(); } bool MprisMediaPlayer2Player::canGoPrevious() const { return mPreviousEnabled; } void MprisMediaPlayer2Player::Previous() { mPreviousAction->trigger(); } bool MprisMediaPlayer2Player::canPause() const { return mSlideShowEnabled; } void MprisMediaPlayer2Player::Pause() { mSlideShow->pause(); } void MprisMediaPlayer2Player::PlayPause() { mToggleSlideShowAction->trigger(); } void MprisMediaPlayer2Player::Stop() { if (mFullScreenAction->isChecked()) { mFullScreenAction->trigger(); } } bool MprisMediaPlayer2Player::canPlay() const { return mSlideShowEnabled; } void MprisMediaPlayer2Player::Play() { if (mSlideShow->isRunning()) { return; } mToggleSlideShowAction->trigger(); } double MprisMediaPlayer2Player::volume() const { return 0; } void MprisMediaPlayer2Player::setVolume(double volume) { Q_UNUSED(volume); } void MprisMediaPlayer2Player::setShuffle(bool isShuffle) { mSlideShow->randomAction()->setChecked(isShuffle); } QVariantMap MprisMediaPlayer2Player::metadata() const { return mMetaData; } qlonglong MprisMediaPlayer2Player::position() const { // milliseconds -> microseconds return mSlideShow->position() * 1000; } double MprisMediaPlayer2Player::rate() const { return 1.0; } void MprisMediaPlayer2Player::setRate(double newRate) { Q_UNUSED(newRate); } double MprisMediaPlayer2Player::minimumRate() const { return MIN_RATE; } double MprisMediaPlayer2Player::maximumRate() const { return MAX_RATE; } bool MprisMediaPlayer2Player::isShuffle() const { return mSlideShow->randomAction()->isChecked(); } bool MprisMediaPlayer2Player::canSeek() const { return false; } bool MprisMediaPlayer2Player::canControl() const { return true; } void MprisMediaPlayer2Player::Seek(qlonglong offset) { Q_UNUSED(offset); } void MprisMediaPlayer2Player::SetPosition(const QDBusObjectPath& trackId, qlonglong pos) { Q_UNUSED(trackId); Q_UNUSED(pos); } void MprisMediaPlayer2Player::OpenUri(const QString& uri) { Q_UNUSED(uri); } void MprisMediaPlayer2Player::onSlideShowStateChanged() { if (!updatePlaybackStatus()) { return; } - signalPropertyChange("Position", position()); - signalPropertyChange("PlaybackStatus", mPlaybackStatus); + signalPropertyChange(QStringLiteral("Position"), position()); + signalPropertyChange(QStringLiteral("PlaybackStatus"), mPlaybackStatus); } void MprisMediaPlayer2Player::onCurrentUrlChanged(const QUrl& url) { if (url.isEmpty()) { mCurrentDocument = Document::Ptr(); } else { mCurrentDocument = DocumentFactory::instance()->load(url); connect(mCurrentDocument.data(), &Document::metaInfoUpdated, this, &MprisMediaPlayer2Player::onMetaInfoUpdated); } onMetaInfoUpdated(); - signalPropertyChange("Position", position()); + signalPropertyChange(QStringLiteral("Position"), position()); } void MprisMediaPlayer2Player::onMetaInfoUpdated() { QVariantMap updatedMetaData; if (mCurrentDocument) { const QUrl url = mCurrentDocument->url(); ImageMetaInfoModel* metaInfoModel = mCurrentDocument->metaInfo(); // We need some unique id mapping to urls. The index in the list is not reliable, // as images can be added/removed during a running slideshow // To allow some bidrectional mapping, convert the url to base64 to encode it for // matching the D-Bus object path spec const QString slideId = QString::fromLatin1(url.toString().toUtf8().toBase64(QByteArray::OmitTrailingEquals)); const QDBusObjectPath trackId(QLatin1String("/org/kde/gwenview/imagelist/") + slideId); updatedMetaData.insert(QStringLiteral("mpris:trackid"), QVariant::fromValue(trackId)); // TODO: for videos also get and report the length if (MimeTypeUtils::urlKind(url) != MimeTypeUtils::KIND_VIDEO) { // convert seconds in microseconds const qlonglong duration = qlonglong(mSlideShow->interval() * 1000000); updatedMetaData.insert(QStringLiteral("mpris:length"), duration); } // TODO: update on metadata changes, given user can edit most of this data const QString name = metaInfoModel->getValueForKey(QStringLiteral("General.Name")); updatedMetaData.insert(QStringLiteral("xesam:title"), name); const QString comment = metaInfoModel->getValueForKey(QStringLiteral("General.Comment")); if (!comment.isEmpty()) { updatedMetaData.insert(QStringLiteral("xesam:comment"), comment); } updatedMetaData.insert(QStringLiteral("xesam:url"), url.toString()); // slight bending of semantics :) const KFileItem folderItem(mContextManager->currentDirUrl()); updatedMetaData.insert(QStringLiteral("xesam:album"), folderItem.text()); // TODO: hook up with thumbnail cache and pass that as arturl // updatedMetaData.insert(QStringLiteral("mpris:artUrl"), url.toString()); #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE const QModelIndex index = mContextManager->dirModel()->indexForUrl(url); if (index.isValid()) { const double rating = index.data(SemanticInfoDirModel::RatingRole).toInt() / 10.0; updatedMetaData.insert(QStringLiteral("xesam:userRating"), rating); } #endif // consider export of other metadata where mapping works } if (updatedMetaData != mMetaData) { mMetaData = updatedMetaData; - signalPropertyChange("Metadata", mMetaData); + signalPropertyChange(QStringLiteral("Metadata"), mMetaData); } } void MprisMediaPlayer2Player::onRandomActionToggled(bool checked) { - signalPropertyChange("Shuffle", checked); + signalPropertyChange(QStringLiteral("Shuffle"), checked); } void MprisMediaPlayer2Player::onFullScreenActionToggled() { if (!updatePlaybackStatus()) { return; } - signalPropertyChange("Position", position()); - signalPropertyChange("PlaybackStatus", mPlaybackStatus); + signalPropertyChange(QStringLiteral("Position"), position()); + signalPropertyChange(QStringLiteral("PlaybackStatus"), mPlaybackStatus); } void MprisMediaPlayer2Player::onToggleSlideShowActionChanged() { const bool isEnabled = mToggleSlideShowAction->isEnabled(); if (mSlideShowEnabled == isEnabled) { return; } mSlideShowEnabled = isEnabled; const bool playbackStatusChanged = updatePlaybackStatus(); - signalPropertyChange("CanPlay", mSlideShowEnabled); - signalPropertyChange("CanPause", mSlideShowEnabled); + signalPropertyChange(QStringLiteral("CanPlay"), mSlideShowEnabled); + signalPropertyChange(QStringLiteral("CanPause"), mSlideShowEnabled); if (playbackStatusChanged) { - signalPropertyChange("Position", position()); - signalPropertyChange("PlaybackStatus", mPlaybackStatus); + signalPropertyChange(QStringLiteral("Position"), position()); + signalPropertyChange(QStringLiteral("PlaybackStatus"), mPlaybackStatus); } } void MprisMediaPlayer2Player::onNextActionChanged() { const bool isEnabled = mNextAction->isEnabled(); if (mNextEnabled == isEnabled) { return; } mNextEnabled = isEnabled; - signalPropertyChange("CanGoNext", mNextEnabled); + signalPropertyChange(QStringLiteral("CanGoNext"), mNextEnabled); } void MprisMediaPlayer2Player::onPreviousActionChanged() { const bool isEnabled = mPreviousAction->isEnabled(); if (mPreviousEnabled == isEnabled) { return; } mPreviousEnabled = isEnabled; - signalPropertyChange("CanGoPrevious", mPreviousEnabled); + signalPropertyChange(QStringLiteral("CanGoPrevious"), mPreviousEnabled); } } diff --git a/lib/placetreemodel.h b/lib/placetreemodel.h index 6ab67be1..029a430c 100644 --- a/lib/placetreemodel.h +++ b/lib/placetreemodel.h @@ -1,72 +1,72 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2009 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, Cambridge, MA 02110-1301, USA. */ #ifndef PLACETREEMODEL_H #define PLACETREEMODEL_H #include // Qt #include // KDE // Local class QUrl; namespace Gwenview { struct PlaceTreeModelPrivate; class GWENVIEWLIB_EXPORT PlaceTreeModel : public QAbstractItemModel { Q_OBJECT public: explicit PlaceTreeModel(QObject*); - ~PlaceTreeModel() Q_DECL_OVERRIDE; - - int columnCount(const QModelIndex& parent = QModelIndex()) const Q_DECL_OVERRIDE; - QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; - QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const Q_DECL_OVERRIDE; - QModelIndex parent(const QModelIndex& index) const Q_DECL_OVERRIDE; - int rowCount(const QModelIndex& parent = QModelIndex()) const Q_DECL_OVERRIDE; - bool hasChildren(const QModelIndex& parent) const Q_DECL_OVERRIDE; - bool canFetchMore(const QModelIndex& parent) const Q_DECL_OVERRIDE; - void fetchMore(const QModelIndex& parent) Q_DECL_OVERRIDE; + ~PlaceTreeModel() override; + + int columnCount(const QModelIndex& parent = QModelIndex()) const override; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex& index) const override; + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + bool hasChildren(const QModelIndex& parent) const override; + bool canFetchMore(const QModelIndex& parent) const override; + void fetchMore(const QModelIndex& parent) override; QUrl urlForIndex(const QModelIndex&) const; private Q_SLOTS: void slotPlacesRowsInserted(const QModelIndex&, int start, int end); void slotPlacesRowsAboutToBeRemoved(const QModelIndex&, int start, int end); void slotDirRowsAboutToBeInserted(const QModelIndex&, int start, int end); void slotDirRowsInserted(const QModelIndex&, int start, int end); void slotDirRowsAboutToBeRemoved(const QModelIndex&, int start, int end); void slotDirRowsRemoved(const QModelIndex&, int start, int end); private: friend struct PlaceTreeModelPrivate; PlaceTreeModelPrivate* const d; }; } // namespace #endif /* PLACETREEMODEL_H */ diff --git a/lib/preferredimagemetainfomodel.h b/lib/preferredimagemetainfomodel.h index 2758c1f2..ed4f941b 100644 --- a/lib/preferredimagemetainfomodel.h +++ b/lib/preferredimagemetainfomodel.h @@ -1,64 +1,64 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 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. */ #ifndef PREFERREDIMAGEMETAINFOMODEL_H #define PREFERREDIMAGEMETAINFOMODEL_H #include // Qt #include // Local #include namespace Gwenview { /** * This model uses an instance of ImageMetaInfoModel to make it possible to * select your preferred image metainfo keys by checking them. */ struct PreferredImageMetaInfoModelPrivate; class GWENVIEWLIB_EXPORT PreferredImageMetaInfoModel : public QSortFilterProxyModel { Q_OBJECT public: PreferredImageMetaInfoModel(ImageMetaInfoModel* model, const QStringList& list); - ~PreferredImageMetaInfoModel() Q_DECL_OVERRIDE; + ~PreferredImageMetaInfoModel() override; - QVariant data(const QModelIndex&, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; - bool setData(const QModelIndex& index, const QVariant& value, int role) Q_DECL_OVERRIDE; - Qt::ItemFlags flags(const QModelIndex& index) const Q_DECL_OVERRIDE; + QVariant data(const QModelIndex&, int role = Qt::DisplayRole) const override; + bool setData(const QModelIndex& index, const QVariant& value, int role) override; + Qt::ItemFlags flags(const QModelIndex& index) const override; Q_SIGNALS: void preferredMetaInfoKeyListChanged(const QStringList&); protected: - bool lessThan(const QModelIndex& left, const QModelIndex& right) const Q_DECL_OVERRIDE; + bool lessThan(const QModelIndex& left, const QModelIndex& right) const override; private: PreferredImageMetaInfoModelPrivate* const d; friend struct PreferredImageMetaInfoModelPrivate; }; } // namespace #endif /* PREFERREDIMAGEMETAINFOMODEL_H */ diff --git a/lib/print/printoptionspage.cpp b/lib/print/printoptionspage.cpp index e96eb886..bc51c2dd 100644 --- a/lib/print/printoptionspage.cpp +++ b/lib/print/printoptionspage.cpp @@ -1,232 +1,232 @@ // 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 "printoptionspage.h" // Qt #include #include #include #include // KDE #include // Local #include #include #include namespace Gwenview { static inline double unitToInches(PrintOptionsPage::Unit unit) { if (unit == PrintOptionsPage::Inches) { return 1.; } else if (unit == PrintOptionsPage::Centimeters) { return 1 / 2.54; } else { // Millimeters return 1 / 25.4; } } struct PrintOptionsPagePrivate : public Ui_PrintOptionsPage { QSize mImageSize; QButtonGroup mScaleGroup; QButtonGroup mPositionGroup; KConfigDialogManager* mConfigDialogManager; void initPositionFrame() { mPositionFrame->setStyleSheet( - "QFrame {" + QStringLiteral("QFrame {" " background-color: palette(mid);" " border: 1px solid palette(dark);" "}" "QToolButton {" " border: none;" " background: palette(base);" "}" "QToolButton:hover {" " background: palette(alternate-base);" " border: 1px solid palette(highlight);" "}" "QToolButton:checked {" " background-color: palette(highlight);" - "}" + "}") ); QGridLayout* layout = new QGridLayout(mPositionFrame); layout->setMargin(0); layout->setSpacing(1); for (int row = 0; row < 3; ++row) { for (int col = 0; col < 3; ++col) { QToolButton* button = new QToolButton(mPositionFrame); button->setFixedSize(40, 40); button->setCheckable(true); layout->addWidget(button, row, col); Qt::Alignment alignment; if (row == 0) { alignment = Qt::AlignTop; } else if (row == 1) { alignment = Qt::AlignVCenter; } else { alignment = Qt::AlignBottom; } if (col == 0) { alignment |= Qt::AlignLeft; } else if (col == 1) { alignment |= Qt::AlignHCenter; } else { alignment |= Qt::AlignRight; } mPositionGroup.addButton(button, int(alignment)); } } } }; PrintOptionsPage::PrintOptionsPage(const QSize& imageSize) : QWidget() , d(new PrintOptionsPagePrivate) { d->setupUi(this); d->mImageSize = imageSize; d->mConfigDialogManager = new KConfigDialogManager(this, GwenviewConfig::self()); d->initPositionFrame(); d->mScaleGroup.addButton(d->mNoScale, NoScale); d->mScaleGroup.addButton(d->mScaleToPage, ScaleToPage); d->mScaleGroup.addButton(d->mScaleTo, ScaleToCustomSize); connect(d->kcfg_PrintWidth, SIGNAL(valueChanged(double)), SLOT(adjustHeightToRatio())); connect(d->kcfg_PrintHeight, SIGNAL(valueChanged(double)), SLOT(adjustWidthToRatio())); connect(d->kcfg_PrintKeepRatio, SIGNAL(toggled(bool)), SLOT(adjustHeightToRatio())); } PrintOptionsPage::~PrintOptionsPage() { delete d; } Qt::Alignment PrintOptionsPage::alignment() const { int id = d->mPositionGroup.checkedId(); qWarning() << "alignment=" << id; return Qt::Alignment(id); } PrintOptionsPage::ScaleMode PrintOptionsPage::scaleMode() const { return PrintOptionsPage::ScaleMode(d->mScaleGroup.checkedId()); } bool PrintOptionsPage::enlargeSmallerImages() const { return d->kcfg_PrintEnlargeSmallerImages->isChecked(); } PrintOptionsPage::Unit PrintOptionsPage::scaleUnit() const { return PrintOptionsPage::Unit(d->kcfg_PrintUnit->currentIndex()); } double PrintOptionsPage::scaleWidth() const { return d->kcfg_PrintWidth->value() * unitToInches(scaleUnit()); } double PrintOptionsPage::scaleHeight() const { return d->kcfg_PrintHeight->value() * unitToInches(scaleUnit()); } void PrintOptionsPage::adjustWidthToRatio() { if (!d->kcfg_PrintKeepRatio->isChecked()) { return; } double width = d->mImageSize.width() * d->kcfg_PrintHeight->value() / d->mImageSize.height(); SignalBlocker blocker(d->kcfg_PrintWidth); d->kcfg_PrintWidth->setValue(width ? width : 1.); } void PrintOptionsPage::adjustHeightToRatio() { if (!d->kcfg_PrintKeepRatio->isChecked()) { return; } double height = d->mImageSize.height() * d->kcfg_PrintWidth->value() / d->mImageSize.width(); SignalBlocker blocker(d->kcfg_PrintHeight); d->kcfg_PrintHeight->setValue(height ? height : 1.); } void PrintOptionsPage::loadConfig() { QAbstractButton* button; button = d->mPositionGroup.button(GwenviewConfig::printPosition()); if (button) { button->setChecked(true); } else { qWarning() << "Unknown button for position group"; } button = d->mScaleGroup.button(GwenviewConfig::printScaleMode()); if (button) { button->setChecked(true); } else { qWarning() << "Unknown button for scale group"; } d->mConfigDialogManager->updateWidgets(); if (d->kcfg_PrintKeepRatio->isChecked()) { adjustHeightToRatio(); } } void PrintOptionsPage::saveConfig() { int position = d->mPositionGroup.checkedId(); GwenviewConfig::setPrintPosition(position); ScaleMode scaleMode = ScaleMode(d->mScaleGroup.checkedId()); GwenviewConfig::setPrintScaleMode(scaleMode); d->mConfigDialogManager->updateSettings(); GwenviewConfig::self()->save(); } } // namespace diff --git a/lib/recentfilesmodel.cpp b/lib/recentfilesmodel.cpp index ca0299c5..a959f07a 100644 --- a/lib/recentfilesmodel.cpp +++ b/lib/recentfilesmodel.cpp @@ -1,116 +1,116 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2009 Aurélien Gâteau Copyright 2014 Tomas Mecir 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 "recentfilesmodel.h" // Qt #include #include #include #include #include // KDE #include #include #include #include // Local #include namespace Gwenview { struct RecentFilesItem : public QStandardItem { QUrl url() const { return mUrl; } RecentFilesItem(const QUrl &url) : mUrl(url) { QString text(mUrl.toDisplayString(QUrl::PreferLocalFile)); #ifdef Q_OS_UNIX - text.replace(QRegularExpression('^' + QDir::homePath()), "~"); + text.replace(QRegularExpression(QStringLiteral("^") + QDir::homePath()), QStringLiteral("~")); #endif setText(text); QMimeDatabase db; const QString iconName = db.mimeTypeForUrl(mUrl).iconName(); setIcon(QIcon::fromTheme(iconName)); setData(mUrl, KFilePlacesModel::UrlRole); KFileItem fileItem(mUrl); setData(QVariant(fileItem), KDirModel::FileItemRole); } private: QUrl mUrl; }; struct RecentFilesModelPrivate { RecentFilesModel* q; QMap mRecentFilesItemForUrl; }; RecentFilesModel::RecentFilesModel(QObject* parent) : QStandardItemModel(parent) , d(new RecentFilesModelPrivate) { d->q = this; } RecentFilesModel::~RecentFilesModel() { delete d; } void RecentFilesModel::addUrl(const QUrl &url) { RecentFilesItem* historyItem = d->mRecentFilesItemForUrl.value(url); if (!historyItem) { historyItem = new RecentFilesItem(url); if (!historyItem) return; d->mRecentFilesItemForUrl.insert(url, historyItem); appendRow(historyItem); } sort(0); } bool RecentFilesModel::removeRows(int start, int count, const QModelIndex& parent) { Q_ASSERT(!parent.isValid()); for (int row = start + count - 1; row >= start ; --row) { RecentFilesItem* historyItem = static_cast(item(row, 0)); Q_ASSERT(historyItem); d->mRecentFilesItemForUrl.remove(historyItem->url()); } return QStandardItemModel::removeRows(start, count, parent); } } // namespace diff --git a/lib/recentfilesmodel.h b/lib/recentfilesmodel.h index 10c553c7..eb31d6bf 100644 --- a/lib/recentfilesmodel.h +++ b/lib/recentfilesmodel.h @@ -1,61 +1,61 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2009 Aurélien Gâteau Copyright 2014 Tomas Mecir 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. */ #ifndef RECENTFILESMODEL_H #define RECENTFILESMODEL_H // Qt #include // KDE // Local #include class QUrl; namespace Gwenview { struct RecentFilesModelPrivate; /** * A model which maintains a list of urls in the dir specified by the * storageDir parameter of its ctor. * Each url is stored in a separate KConfig file to avoid concurrency issues. */ class GWENVIEWLIB_EXPORT RecentFilesModel : public QStandardItemModel { Q_OBJECT public: explicit RecentFilesModel(QObject* parent); - ~RecentFilesModel() Q_DECL_OVERRIDE; + ~RecentFilesModel() override; void addUrl(const QUrl&); - bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()) Q_DECL_OVERRIDE; + bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()) override; private: RecentFilesModelPrivate* const d; }; } // namespace #endif /* RECENTFILESMODEL_H */ diff --git a/lib/recursivedirmodel.h b/lib/recursivedirmodel.h index 22e1b261..89c020b1 100644 --- a/lib/recursivedirmodel.h +++ b/lib/recursivedirmodel.h @@ -1,69 +1,69 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2012 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, Cambridge, MA 02110-1301, USA. */ #ifndef RECURSIVEDIRMODEL_H #define RECURSIVEDIRMODEL_H // Local #include // KDE #include // Qt #include class QUrl; namespace Gwenview { struct RecursiveDirModelPrivate; /** * Recursively list content of a dir */ class GWENVIEWLIB_EXPORT RecursiveDirModel : public QAbstractListModel { Q_OBJECT public: explicit RecursiveDirModel(QObject* parent = nullptr); - ~RecursiveDirModel() Q_DECL_OVERRIDE; + ~RecursiveDirModel() override; QUrl url() const; void setUrl(const QUrl&); - int rowCount(const QModelIndex&) const Q_DECL_OVERRIDE; - QVariant data(const QModelIndex&, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; + int rowCount(const QModelIndex&) const override; + QVariant data(const QModelIndex&, int role = Qt::DisplayRole) const override; Q_SIGNALS: void completed(); private Q_SLOTS: void slotItemsAdded(const QUrl &dirUrl, const KFileItemList&); void slotItemsDeleted(const KFileItemList&); void slotDirCleared(const QUrl&); void slotCleared(); private: RecursiveDirModelPrivate* const d; }; } // namespace #endif /* RECURSIVEDIRMODEL_H */ diff --git a/lib/redeyereduction/redeyereductionimageoperation.cpp b/lib/redeyereduction/redeyereductionimageoperation.cpp index 76b84fc0..482af005 100644 --- a/lib/redeyereduction/redeyereductionimageoperation.cpp +++ b/lib/redeyereduction/redeyereductionimageoperation.cpp @@ -1,167 +1,167 @@ // 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 "redeyereductionimageoperation.h" // Stdc #include // Qt #include #include #include // KDE #include // Local #include "ramp.h" #include "document/document.h" #include "document/documentjob.h" #include "document/abstractdocumenteditor.h" #include "paintutils.h" namespace Gwenview { class RedEyeReductionJob : public ThreadedDocumentJob { public: RedEyeReductionJob(const QRectF& rectF) : mRectF(rectF) {} - void threadedStart() Q_DECL_OVERRIDE + void threadedStart() override { if (!checkDocumentEditor()) { return; } QImage img = document()->image(); RedEyeReductionImageOperation::apply(&img, mRectF); document()->editor()->setImage(img); setError(NoError); } private: QRectF mRectF; }; struct RedEyeReductionImageOperationPrivate { QRectF mRectF; QImage mOriginalImage; }; RedEyeReductionImageOperation::RedEyeReductionImageOperation(const QRectF& rectF) : d(new RedEyeReductionImageOperationPrivate) { d->mRectF = rectF; setText(i18n("RedEyeReduction")); } RedEyeReductionImageOperation::~RedEyeReductionImageOperation() { delete d; } void RedEyeReductionImageOperation::redo() { QImage img = document()->image(); QRect rect = PaintUtils::containingRect(d->mRectF); d->mOriginalImage = img.copy(rect); redoAsDocumentJob(new RedEyeReductionJob(d->mRectF)); } void RedEyeReductionImageOperation::undo() { if (!document()->editor()) { qWarning() << "!document->editor()"; return; } QImage img = document()->image(); { QPainter painter(&img); painter.setCompositionMode(QPainter::CompositionMode_Source); QRect rect = PaintUtils::containingRect(d->mRectF); painter.drawImage(rect.topLeft(), d->mOriginalImage); } document()->editor()->setImage(img); finish(true); } /** * This code is inspired from code found in a Paint.net plugin: * http://paintdotnet.forumer.com/viewtopic.php?f=27&t=26193&p=205954&hilit=red+eye#p205954 */ inline qreal computeRedEyeAlpha(const QColor& src) { int hue, sat, value; src.getHsv(&hue, &sat, &value); qreal axs = 1.0; if (hue > 259) { static const Ramp ramp(30, 35, 0., 1.); axs = ramp(sat); } else { const Ramp ramp(hue * 2 + 29, hue * 2 + 40, 0., 1.); axs = ramp(sat); } return qBound(qreal(0.), src.alphaF() * axs, qreal(1.)); } void RedEyeReductionImageOperation::apply(QImage* img, const QRectF& rectF) { const QRect rect = PaintUtils::containingRect(rectF); const qreal radius = rectF.width() / 2; const qreal centerX = rectF.x() + radius; const qreal centerY = rectF.y() + radius; const Ramp radiusRamp( qMin(qreal(radius * 0.7), qreal(radius - 1)), radius, qreal(1.), qreal(0.)); uchar* line = img->scanLine(rect.top()) + rect.left() * 4; for (int y = rect.top(); y < rect.bottom(); ++y, line += img->bytesPerLine()) { QRgb* ptr = (QRgb*)line; for (int x = rect.left(); x < rect.right(); ++x, ++ptr) { const qreal currentRadius = sqrt(pow(y - centerY, 2) + pow(x - centerX, 2)); qreal alpha = radiusRamp(currentRadius); if (qFuzzyCompare(alpha, 0)) { continue; } const QColor src(*ptr); alpha *= computeRedEyeAlpha(src); int r = src.red(); int g = src.green(); int b = src.blue(); QColor dst; // Replace red with green, and blend according to alpha dst.setRed(int((1 - alpha) * r + alpha * g)); dst.setGreen(g); dst.setBlue(b); *ptr = dst.rgba(); } } } } // namespace diff --git a/lib/redeyereduction/redeyereductionimageoperation.h b/lib/redeyereduction/redeyereductionimageoperation.h index 3846bd99..020b4e78 100644 --- a/lib/redeyereduction/redeyereductionimageoperation.h +++ b/lib/redeyereduction/redeyereductionimageoperation.h @@ -1,56 +1,56 @@ // 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. */ #ifndef REDEYEREDUCTIONIMAGEOPERATION_H #define REDEYEREDUCTIONIMAGEOPERATION_H #include // Qt // KDE // Local #include class QRectF; namespace Gwenview { struct RedEyeReductionImageOperationPrivate; class GWENVIEWLIB_EXPORT RedEyeReductionImageOperation : public AbstractImageOperation { public: RedEyeReductionImageOperation(const QRectF&); - ~RedEyeReductionImageOperation() Q_DECL_OVERRIDE; + ~RedEyeReductionImageOperation() override; - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; + void redo() override; + void undo() override; static void apply(QImage* img, const QRectF& rectF); private: RedEyeReductionImageOperationPrivate* const d; }; } // namespace #endif /* REDEYEREDUCTIONIMAGEOPERATION_H */ diff --git a/lib/redeyereduction/redeyereductiontool.h b/lib/redeyereduction/redeyereductiontool.h index 3a758940..97cb53bb 100644 --- a/lib/redeyereduction/redeyereductiontool.h +++ b/lib/redeyereduction/redeyereductiontool.h @@ -1,77 +1,77 @@ // 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. */ #ifndef REDEYEREDUCTIONTOOL_H #define REDEYEREDUCTIONTOOL_H #include // Qt // KDE // Local #include namespace Gwenview { class AbstractImageOperation; class RasterImageView; struct RedEyeReductionToolPrivate; class GWENVIEWLIB_EXPORT RedEyeReductionTool : public AbstractRasterImageViewTool { Q_OBJECT public: enum Status { NotSet, Adjusting }; explicit RedEyeReductionTool(RasterImageView* parent); - ~RedEyeReductionTool() Q_DECL_OVERRIDE; + ~RedEyeReductionTool() override; - void paint(QPainter*) Q_DECL_OVERRIDE; + void paint(QPainter*) override; - void mousePressEvent(QGraphicsSceneMouseEvent*) Q_DECL_OVERRIDE; - void mouseMoveEvent(QGraphicsSceneMouseEvent*) Q_DECL_OVERRIDE; - void mouseReleaseEvent(QGraphicsSceneMouseEvent*) Q_DECL_OVERRIDE; - void keyPressEvent(QKeyEvent*) Q_DECL_OVERRIDE; + void mousePressEvent(QGraphicsSceneMouseEvent*) override; + void mouseMoveEvent(QGraphicsSceneMouseEvent*) override; + void mouseReleaseEvent(QGraphicsSceneMouseEvent*) override; + void keyPressEvent(QKeyEvent*) override; - void toolActivated() Q_DECL_OVERRIDE; + void toolActivated() override; - QWidget* widget() const Q_DECL_OVERRIDE; + QWidget* widget() const override; Q_SIGNALS: void done(); void imageOperationRequested(AbstractImageOperation*); private Q_SLOTS: void setDiameter(int); void slotApplyClicked(); private: RedEyeReductionToolPrivate* const d; }; } // namespace #endif /* REDEYEREDUCTIONTOOL_H */ diff --git a/lib/resize/resizeimagedialog.cpp b/lib/resize/resizeimagedialog.cpp index 08be63dc..1b24d893 100644 --- a/lib/resize/resizeimagedialog.cpp +++ b/lib/resize/resizeimagedialog.cpp @@ -1,187 +1,187 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2010 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, Cambridge, MA 02110-1301, USA. */ // Self #include "resizeimagedialog.h" // Qt #include #include // KDE #include #include // Local #include namespace Gwenview { struct ResizeImageDialogPrivate : public Ui_ResizeImageWidget { bool mUpdateFromRatio; bool mUpdateFromSizeOrPercentage; QSize mOriginalSize; }; ResizeImageDialog::ResizeImageDialog(QWidget* parent) : QDialog(parent) , d(new ResizeImageDialogPrivate) { d->mUpdateFromRatio = false; d->mUpdateFromSizeOrPercentage = false; QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); mainLayout->setSizeConstraint(QLayout::SetFixedSize); QWidget* content = new QWidget(this); d->setupUi(content); mainLayout->addWidget(content); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, this, &ResizeImageDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &ResizeImageDialog::reject); mainLayout->addWidget(buttonBox); content->layout()->setMargin(0); - KGuiItem::assign(okButton, KGuiItem(i18n("Resize"), "transform-scale")); + KGuiItem::assign(okButton, KGuiItem(i18n("Resize"), QStringLiteral("transform-scale"))); setWindowTitle(content->windowTitle()); d->mWidthSpinBox->setFocus(); connect(d->mWidthSpinBox, static_cast(&QSpinBox::valueChanged), this, &ResizeImageDialog::slotWidthChanged); connect(d->mHeightSpinBox, static_cast(&QSpinBox::valueChanged), this, &ResizeImageDialog::slotHeightChanged); connect(d->mWidthPercentSpinBox, static_cast(&QDoubleSpinBox::valueChanged), this, &ResizeImageDialog::slotWidthPercentChanged); connect(d->mHeightPercentSpinBox, static_cast(&QDoubleSpinBox::valueChanged), this, &ResizeImageDialog::slotHeightPercentChanged); connect(d->mKeepAspectCheckBox, &QCheckBox::toggled, this, &ResizeImageDialog::slotKeepAspectChanged); } ResizeImageDialog::~ResizeImageDialog() { delete d; } void ResizeImageDialog::setOriginalSize(const QSize& size) { d->mOriginalSize = size; d->mOriginalWidthLabel->setText(QString::number(size.width()) + QStringLiteral(" px")); d->mOriginalHeightLabel->setText(QString::number(size.height()) + QStringLiteral(" px")); d->mWidthSpinBox->setValue(size.width()); d->mHeightSpinBox->setValue(size.height()); } QSize ResizeImageDialog::size() const { return QSize( d->mWidthSpinBox->value(), d->mHeightSpinBox->value() ); } void ResizeImageDialog::slotWidthChanged(int width) { // Update width percentage to match width, only if this was a manual adjustment if (!d->mUpdateFromSizeOrPercentage && !d->mUpdateFromRatio) { d->mUpdateFromSizeOrPercentage = true; d->mWidthPercentSpinBox->setValue((double(width) / d->mOriginalSize.width()) * 100); d->mUpdateFromSizeOrPercentage = false; } if (!d->mKeepAspectCheckBox->isChecked() || d->mUpdateFromRatio) { return; } // Update height to keep ratio, only if ratio locked and this was a manual adjustment d->mUpdateFromRatio = true; d->mHeightSpinBox->setValue(d->mOriginalSize.height() * width / d->mOriginalSize.width()); d->mUpdateFromRatio = false; } void ResizeImageDialog::slotHeightChanged(int height) { // Update height percentage to match height, only if this was a manual adjustment if (!d->mUpdateFromSizeOrPercentage && !d->mUpdateFromRatio) { d->mUpdateFromSizeOrPercentage = true; d->mHeightPercentSpinBox->setValue((double(height) / d->mOriginalSize.height()) * 100); d->mUpdateFromSizeOrPercentage = false; } if (!d->mKeepAspectCheckBox->isChecked() || d->mUpdateFromRatio) { return; } // Update width to keep ratio, only if ratio locked and this was a manual adjustment d->mUpdateFromRatio = true; d->mWidthSpinBox->setValue(d->mOriginalSize.width() * height / d->mOriginalSize.height()); d->mUpdateFromRatio = false; } void ResizeImageDialog::slotWidthPercentChanged(double widthPercent) { // Update width to match width percentage, only if this was a manual adjustment if (!d->mUpdateFromSizeOrPercentage && !d->mUpdateFromRatio) { d->mUpdateFromSizeOrPercentage = true; d->mWidthSpinBox->setValue((widthPercent / 100) * d->mOriginalSize.width()); d->mUpdateFromSizeOrPercentage = false; } if (!d->mKeepAspectCheckBox->isChecked() || d->mUpdateFromRatio) { return; } // Keep height percentage in sync with width percentage, only if ratio locked and this was a manual adjustment d->mUpdateFromRatio = true; d->mHeightPercentSpinBox->setValue(d->mWidthPercentSpinBox->value()); d->mUpdateFromRatio = false; } void ResizeImageDialog::slotHeightPercentChanged(double heightPercent) { // Update height to match height percentage, only if this was a manual adjustment if (!d->mUpdateFromSizeOrPercentage && !d->mUpdateFromRatio) { d->mUpdateFromSizeOrPercentage = true; d->mHeightSpinBox->setValue((heightPercent / 100) * d->mOriginalSize.height()); d->mUpdateFromSizeOrPercentage = false; } if (!d->mKeepAspectCheckBox->isChecked() || d->mUpdateFromRatio) { return; } // Keep height percentage in sync with width percentage, only if ratio locked and this was a manual adjustment d->mUpdateFromRatio = true; d->mWidthPercentSpinBox->setValue(d->mHeightPercentSpinBox->value()); d->mUpdateFromRatio = false; } void ResizeImageDialog::slotKeepAspectChanged(bool value) { if (value) { d->mUpdateFromSizeOrPercentage = true; slotWidthChanged(d->mWidthSpinBox->value()); slotWidthPercentChanged(d->mWidthPercentSpinBox->value()); d->mUpdateFromSizeOrPercentage = false; } } } // namespace diff --git a/lib/resize/resizeimageoperation.cpp b/lib/resize/resizeimageoperation.cpp index 0e23c093..32cdb83f 100644 --- a/lib/resize/resizeimageoperation.cpp +++ b/lib/resize/resizeimageoperation.cpp @@ -1,95 +1,95 @@ // 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 "resizeimageoperation.h" // Qt #include #include // KDE #include // Local #include "document/abstractdocumenteditor.h" #include "document/document.h" #include "document/documentjob.h" namespace Gwenview { struct ResizeImageOperationPrivate { QSize mSize; QImage mOriginalImage; }; class ResizeJob : public ThreadedDocumentJob { public: ResizeJob(const QSize& size) : mSize(size) {} - void threadedStart() Q_DECL_OVERRIDE + void threadedStart() override { if (!checkDocumentEditor()) { return; } QImage image = document()->image(); image = image.scaled(mSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); document()->editor()->setImage(image); setError(NoError); } private: QSize mSize; }; ResizeImageOperation::ResizeImageOperation(const QSize& size) : d(new ResizeImageOperationPrivate) { d->mSize = size; setText(i18nc("(qtundo-format)", "Resize")); } ResizeImageOperation::~ResizeImageOperation() { delete d; } void ResizeImageOperation::redo() { d->mOriginalImage = document()->image(); redoAsDocumentJob(new ResizeJob(d->mSize)); } void ResizeImageOperation::undo() { if (!document()->editor()) { qWarning() << "!document->editor()"; return; } document()->editor()->setImage(d->mOriginalImage); finish(true); } } // namespace diff --git a/lib/resize/resizeimageoperation.h b/lib/resize/resizeimageoperation.h index 612b6817..19e868da 100644 --- a/lib/resize/resizeimageoperation.h +++ b/lib/resize/resizeimageoperation.h @@ -1,52 +1,52 @@ // 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. */ #ifndef RESIZEIMAGEOPERATION_H #define RESIZEIMAGEOPERATION_H #include // Qt // KDE // Local #include namespace Gwenview { struct ResizeImageOperationPrivate; class GWENVIEWLIB_EXPORT ResizeImageOperation : public AbstractImageOperation { public: ResizeImageOperation(const QSize& size); - ~ResizeImageOperation() Q_DECL_OVERRIDE; + ~ResizeImageOperation() override; - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; + void redo() override; + void undo() override; private: ResizeImageOperationPrivate* const d; }; } // namespace #endif /* RESIZEIMAGEOPERATION_H */ diff --git a/lib/semanticinfo/baloosemanticinfobackend.h b/lib/semanticinfo/baloosemanticinfobackend.h index 9318c87a..7b4af5c2 100644 --- a/lib/semanticinfo/baloosemanticinfobackend.h +++ b/lib/semanticinfo/baloosemanticinfobackend.h @@ -1,67 +1,67 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 Aurélien Gâteau Copyright 2014 Vishesh Handa 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. */ #ifndef BALOOSEMANTICINFOBACKEND_H #define BALOOSEMANTICINFOBACKEND_H #include // Qt // KDE // Local #include "abstractsemanticinfobackend.h" namespace Gwenview { /** * A real metadata backend using Baloo to store and retrieve metadata. */ class GWENVIEWLIB_EXPORT BalooSemanticInfoBackend : public AbstractSemanticInfoBackEnd { Q_OBJECT public: explicit BalooSemanticInfoBackend(QObject* parent); - ~BalooSemanticInfoBackend() Q_DECL_OVERRIDE; + ~BalooSemanticInfoBackend() override; - TagSet allTags() const Q_DECL_OVERRIDE; + TagSet allTags() const override; - void refreshAllTags() Q_DECL_OVERRIDE; + void refreshAllTags() override; - void storeSemanticInfo(const QUrl&, const SemanticInfo&) Q_DECL_OVERRIDE; + void storeSemanticInfo(const QUrl&, const SemanticInfo&) override; - void retrieveSemanticInfo(const QUrl&) Q_DECL_OVERRIDE; + void retrieveSemanticInfo(const QUrl&) override; - QString labelForTag(const SemanticInfoTag&) const Q_DECL_OVERRIDE; + QString labelForTag(const SemanticInfoTag&) const override; - SemanticInfoTag tagForLabel(const QString&) Q_DECL_OVERRIDE; + SemanticInfoTag tagForLabel(const QString&) override; private: struct Private; Private* const d; }; } // namespace #endif /* BALOOSEMANTICINFOBACKEND_H */ diff --git a/lib/semanticinfo/semanticinfodirmodel.h b/lib/semanticinfo/semanticinfodirmodel.h index 59cd938f..6f9577a8 100644 --- a/lib/semanticinfo/semanticinfodirmodel.h +++ b/lib/semanticinfo/semanticinfodirmodel.h @@ -1,84 +1,84 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 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, Cambridge, MA 02110-1301, USA. */ #ifndef SEMANTICINFODIRMODEL_H #define SEMANTICINFODIRMODEL_H // Qt // KDE #include // Local class QUrl; namespace Gwenview { class AbstractSemanticInfoBackEnd; struct SemanticInfo; struct SemanticInfoDirModelPrivate; /** * Extends KDirModel by providing read/write access to image metadata such as * rating, tags and descriptions. */ class SemanticInfoDirModel : public KDirModel { Q_OBJECT public: enum { RatingRole = 0x21a43a51, DescriptionRole = 0x26FB33FA, TagsRole = 0x0462F0A8 }; SemanticInfoDirModel(QObject* parent); ~SemanticInfoDirModel(); void clearSemanticInfoCache(); bool semanticInfoAvailableForIndex(const QModelIndex&) const; void retrieveSemanticInfoForIndex(const QModelIndex&); SemanticInfo semanticInfoForIndex(const QModelIndex&) const; - QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; - bool setData(const QModelIndex& index, const QVariant& data, int role = Qt::EditRole) Q_DECL_OVERRIDE; + bool setData(const QModelIndex& index, const QVariant& data, int role = Qt::EditRole) override; AbstractSemanticInfoBackEnd* semanticInfoBackEnd() const; Q_SIGNALS: void semanticInfoRetrieved(const QUrl&, const SemanticInfo&); private: SemanticInfoDirModelPrivate* const d; private Q_SLOTS: void slotSemanticInfoRetrieved(const QUrl &url, const SemanticInfo&); void slotRowsAboutToBeRemoved(const QModelIndex&, int, int); void slotModelAboutToBeReset(); }; } // namespace #endif /* SEMANTICINFODIRMODEL_H */ diff --git a/lib/semanticinfo/sorteddirmodel.cpp b/lib/semanticinfo/sorteddirmodel.cpp index 188af143..708b1018 100644 --- a/lib/semanticinfo/sorteddirmodel.cpp +++ b/lib/semanticinfo/sorteddirmodel.cpp @@ -1,319 +1,319 @@ /* 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. */ #include "sorteddirmodel.h" #include // Qt #include #include #include // KDE #include // Local #include #include #ifdef GWENVIEW_SEMANTICINFO_BACKEND_NONE #include #else #include "abstractsemanticinfobackend.h" #include "semanticinfodirmodel.h" #include #endif namespace Gwenview { AbstractSortedDirModelFilter::AbstractSortedDirModelFilter(SortedDirModel* model) : QObject(model) , mModel(model) { if (mModel) { mModel->addFilter(this); } } AbstractSortedDirModelFilter::~AbstractSortedDirModelFilter() { if (mModel) { mModel->removeFilter(this); } } struct SortedDirModelPrivate { #ifdef GWENVIEW_SEMANTICINFO_BACKEND_NONE KDirModel* mSourceModel; #else SemanticInfoDirModel* mSourceModel; #endif QStringList mBlackListedExtensions; QList mFilters; QTimer mDelayedApplyFiltersTimer; MimeTypeUtils::Kinds mKindFilter; }; SortedDirModel::SortedDirModel(QObject* parent) : KDirSortFilterProxyModel(parent) , d(new SortedDirModelPrivate) { #ifdef GWENVIEW_SEMANTICINFO_BACKEND_NONE d->mSourceModel = new KDirModel(this); #else d->mSourceModel = new SemanticInfoDirModel(this); #endif setSourceModel(d->mSourceModel); d->mDelayedApplyFiltersTimer.setInterval(0); d->mDelayedApplyFiltersTimer.setSingleShot(true); connect(&d->mDelayedApplyFiltersTimer, &QTimer::timeout, this, &SortedDirModel::doApplyFilters); } SortedDirModel::~SortedDirModel() { delete d; } MimeTypeUtils::Kinds SortedDirModel::kindFilter() const { return d->mKindFilter; } void SortedDirModel::setKindFilter(MimeTypeUtils::Kinds kindFilter) { if (d->mKindFilter == kindFilter) { return; } d->mKindFilter = kindFilter; applyFilters(); } void SortedDirModel::adjustKindFilter(MimeTypeUtils::Kinds kinds, bool set) { MimeTypeUtils::Kinds kindFilter = d->mKindFilter; if (set) { kindFilter |= kinds; } else { kindFilter &= ~kinds; } setKindFilter(kindFilter); } void SortedDirModel::addFilter(AbstractSortedDirModelFilter* filter) { d->mFilters << filter; applyFilters(); } void SortedDirModel::removeFilter(AbstractSortedDirModelFilter* filter) { d->mFilters.removeAll(filter); applyFilters(); } KDirLister* SortedDirModel::dirLister() const { return d->mSourceModel->dirLister(); } void SortedDirModel::reload() { #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE d->mSourceModel->clearSemanticInfoCache(); #endif dirLister()->updateDirectory(dirLister()->url()); } void SortedDirModel::setBlackListedExtensions(const QStringList& list) { d->mBlackListedExtensions = list; } KFileItem SortedDirModel::itemForIndex(const QModelIndex& index) const { if (!index.isValid()) { return KFileItem(); } QModelIndex sourceIndex = mapToSource(index); return d->mSourceModel->itemForIndex(sourceIndex); } QUrl SortedDirModel::urlForIndex(const QModelIndex& index) const { KFileItem item = itemForIndex(index); return item.isNull() ? QUrl() : item.url(); } KFileItem SortedDirModel::itemForSourceIndex(const QModelIndex& sourceIndex) const { if (!sourceIndex.isValid()) { return KFileItem(); } return d->mSourceModel->itemForIndex(sourceIndex); } QModelIndex SortedDirModel::indexForItem(const KFileItem& item) const { if (item.isNull()) { return QModelIndex(); } QModelIndex sourceIndex = d->mSourceModel->indexForItem(item); return mapFromSource(sourceIndex); } QModelIndex SortedDirModel::indexForUrl(const QUrl &url) const { if (!url.isValid()) { return QModelIndex(); } QModelIndex sourceIndex = d->mSourceModel->indexForUrl(url); return mapFromSource(sourceIndex); } bool SortedDirModel::filterAcceptsRow(int row, const QModelIndex& parent) const { QModelIndex index = d->mSourceModel->index(row, 0, parent); KFileItem fileItem = d->mSourceModel->itemForIndex(index); MimeTypeUtils::Kinds kind = MimeTypeUtils::fileItemKind(fileItem); if (d->mKindFilter != MimeTypeUtils::Kinds() && !(d->mKindFilter & kind)) { return false; } if (kind != MimeTypeUtils::KIND_DIR && kind != MimeTypeUtils::KIND_ARCHIVE) { - int dotPos = fileItem.name().lastIndexOf('.'); + int dotPos = fileItem.name().lastIndexOf(QLatin1Char('.')); if (dotPos >= 1) { QString extension = fileItem.name().mid(dotPos + 1).toLower(); if (d->mBlackListedExtensions.contains(extension)) { return false; } } #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE if (!d->mSourceModel->semanticInfoAvailableForIndex(index)) { Q_FOREACH(const AbstractSortedDirModelFilter * filter, d->mFilters) { // Make sure we have semanticinfo, otherwise retrieve it and // return false, we will be called again later when it is // there. if (filter->needsSemanticInfo()) { d->mSourceModel->retrieveSemanticInfoForIndex(index); return false; } } } #endif Q_FOREACH(const AbstractSortedDirModelFilter * filter, d->mFilters) { if (!filter->acceptsIndex(index)) { return false; } } } return KDirSortFilterProxyModel::filterAcceptsRow(row, parent); } AbstractSemanticInfoBackEnd* SortedDirModel::semanticInfoBackEnd() const { #ifdef GWENVIEW_SEMANTICINFO_BACKEND_NONE return 0; #else return d->mSourceModel->semanticInfoBackEnd(); #endif } #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE SemanticInfo SortedDirModel::semanticInfoForSourceIndex(const QModelIndex& sourceIndex) const { return d->mSourceModel->semanticInfoForIndex(sourceIndex); } #endif void SortedDirModel::applyFilters() { d->mDelayedApplyFiltersTimer.start(); } void SortedDirModel::doApplyFilters() { QSortFilterProxyModel::invalidateFilter(); } bool SortedDirModel::lessThan(const QModelIndex& left, const QModelIndex& right) const { const KFileItem leftItem = itemForSourceIndex(left); const KFileItem rightItem = itemForSourceIndex(right); const bool leftIsDirOrArchive = ArchiveUtils::fileItemIsDirOrArchive(leftItem); const bool rightIsDirOrArchive = ArchiveUtils::fileItemIsDirOrArchive(rightItem); if (leftIsDirOrArchive != rightIsDirOrArchive) { return sortOrder() == Qt::AscendingOrder ? leftIsDirOrArchive : rightIsDirOrArchive; } // Apply special sort handling only to images. For folders/archives or when // a secondary criterion is needed, delegate sorting to the parent class. if (!leftIsDirOrArchive) { if (sortColumn() == KDirModel::ModifiedTime) { const QDateTime leftDate = TimeUtils::dateTimeForFileItem(leftItem); const QDateTime rightDate = TimeUtils::dateTimeForFileItem(rightItem); if (leftDate != rightDate) { return leftDate < rightDate; } } #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE if (sortRole() == SemanticInfoDirModel::RatingRole) { const int leftRating = d->mSourceModel->data(left, SemanticInfoDirModel::RatingRole).toInt(); const int rightRating = d->mSourceModel->data(right, SemanticInfoDirModel::RatingRole).toInt(); if (leftRating != rightRating) { return leftRating < rightRating; } } #endif } return KDirSortFilterProxyModel::lessThan(left, right); } bool SortedDirModel::hasDocuments() const { const int count = rowCount(); if (count == 0) { return false; } for (int row = 0; row < count; ++row) { const QModelIndex idx = index(row, 0); const KFileItem item = itemForIndex(idx); if (!ArchiveUtils::fileItemIsDirOrArchive(item)) { return true; } } return false; } void SortedDirModel::setDirLister(KDirLister* dirLister) { d->mSourceModel->setDirLister(dirLister); } } //namespace diff --git a/lib/semanticinfo/sorteddirmodel.h b/lib/semanticinfo/sorteddirmodel.h index e2da2adf..9765e99b 100644 --- a/lib/semanticinfo/sorteddirmodel.h +++ b/lib/semanticinfo/sorteddirmodel.h @@ -1,133 +1,133 @@ /* 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. */ #ifndef SORTEDDIRMODEL_H #define SORTEDDIRMODEL_H #include // Qt #include // KDE #include // Local #include #include class KDirLister; class KFileItem; class QUrl; namespace Gwenview { class AbstractSemanticInfoBackEnd; struct SortedDirModelPrivate; #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE struct SemanticInfo; #endif class SortedDirModel; class GWENVIEWLIB_EXPORT AbstractSortedDirModelFilter : public QObject { public: AbstractSortedDirModelFilter(SortedDirModel* model); ~AbstractSortedDirModelFilter(); SortedDirModel* model() const { return mModel; } virtual bool needsSemanticInfo() const = 0; /** * Returns true if index should be accepted. * Warning: index is a source index of SortedDirModel */ virtual bool acceptsIndex(const QModelIndex& index) const = 0; private: QPointer mModel; }; /** * This model makes it possible to show all images in a folder. * It can filter images based on name and metadata. */ class GWENVIEWLIB_EXPORT SortedDirModel : public KDirSortFilterProxyModel { Q_OBJECT public: explicit SortedDirModel(QObject* parent = nullptr); - ~SortedDirModel(); + ~SortedDirModel() override; KDirLister* dirLister() const; /** * Redefines the dir lister, useful for debugging */ void setDirLister(KDirLister*); KFileItem itemForIndex(const QModelIndex& index) const; QUrl urlForIndex(const QModelIndex& index) const; KFileItem itemForSourceIndex(const QModelIndex& sourceIndex) const; QModelIndex indexForItem(const KFileItem& item) const; QModelIndex indexForUrl(const QUrl &url) const; void setKindFilter(MimeTypeUtils::Kinds); MimeTypeUtils::Kinds kindFilter() const; void adjustKindFilter(MimeTypeUtils::Kinds, bool set); /** * A list of file extensions we should skip */ void setBlackListedExtensions(const QStringList& list); void addFilter(AbstractSortedDirModelFilter*); void removeFilter(AbstractSortedDirModelFilter*); void reload(); AbstractSemanticInfoBackEnd* semanticInfoBackEnd() const; #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE SemanticInfo semanticInfoForSourceIndex(const QModelIndex& sourceIndex) const; #endif bool hasDocuments() const; public Q_SLOTS: void applyFilters(); protected: - bool filterAcceptsRow(int row, const QModelIndex& parent) const Q_DECL_OVERRIDE; - bool lessThan(const QModelIndex& left, const QModelIndex& right) const Q_DECL_OVERRIDE; + bool filterAcceptsRow(int row, const QModelIndex& parent) const override; + bool lessThan(const QModelIndex& left, const QModelIndex& right) const override; private Q_SLOTS: void doApplyFilters(); private: friend struct SortedDirModelPrivate; SortedDirModelPrivate * const d; }; } // namespace #endif /* SORTEDDIRMODEL_H */ diff --git a/lib/semanticinfo/tagitemdelegate.h b/lib/semanticinfo/tagitemdelegate.h index bae924cd..7079e2d0 100644 --- a/lib/semanticinfo/tagitemdelegate.h +++ b/lib/semanticinfo/tagitemdelegate.h @@ -1,67 +1,67 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 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, Cambridge, MA 02110-1301, USA. */ #ifndef TAGITEMDELEGATE_H #define TAGITEMDELEGATE_H // KDE #include namespace Gwenview { typedef QString SemanticInfoTag; class TagItemDelegate : public KWidgetItemDelegate { Q_OBJECT public: TagItemDelegate(QAbstractItemView* view); protected: - QList createItemWidgets(const QModelIndex &index) const Q_DECL_OVERRIDE; + QList createItemWidgets(const QModelIndex &index) const override; virtual void updateItemWidgets(const QList widgets, const QStyleOptionViewItem& option, - const QPersistentModelIndex& /*index*/) const Q_DECL_OVERRIDE; + const QPersistentModelIndex& /*index*/) const override; virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, - const QModelIndex &index) const Q_DECL_OVERRIDE; + const QModelIndex &index) const override; virtual QSize sizeHint(const QStyleOptionViewItem &/*option*/, - const QModelIndex &/*index*/) const Q_DECL_OVERRIDE; + const QModelIndex &/*index*/) const override; Q_SIGNALS: void removeTagRequested(const SemanticInfoTag& tag); void assignTagToAllRequested(const SemanticInfoTag& tag); private Q_SLOTS: void slotRemoveButtonClicked(); void slotAssignToAllButtonClicked(); private: int mButtonSize; int mMargin; int mSpacing; }; } // namespace #endif /* TAGITEMDELEGATE_H */ diff --git a/lib/semanticinfo/tagwidget.cpp b/lib/semanticinfo/tagwidget.cpp index aefd0477..6fb9ff10 100644 --- a/lib/semanticinfo/tagwidget.cpp +++ b/lib/semanticinfo/tagwidget.cpp @@ -1,257 +1,257 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 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, Cambridge, MA 02110-1301, USA. */ // Self #include "tagwidget.h" // Qt #include #include #include #include #include #include #include #include #include #include #include // KDE #include // Local #include #include namespace Gwenview { class TagCompleterModel : public QSortFilterProxyModel { public: TagCompleterModel(QObject* parent) : QSortFilterProxyModel(parent) { } void setTagInfo(const TagInfo& tagInfo) { mExcludedTagSet.clear(); TagInfo::ConstIterator it = tagInfo.begin(), end = tagInfo.end(); for (; it != end; ++it) { if (it.value()) { mExcludedTagSet << it.key(); } } invalidate(); } void setSemanticInfoBackEnd(AbstractSemanticInfoBackEnd* backEnd) { setSourceModel(TagModel::createAllTagsModel(this, backEnd)); } protected: - bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const Q_DECL_OVERRIDE + bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override { QModelIndex sourceIndex = sourceModel()->index(sourceRow, 0, sourceParent); SemanticInfoTag tag = sourceIndex.data(TagModel::TagRole).toString(); return !mExcludedTagSet.contains(tag); } private: TagSet mExcludedTagSet; }; /** * A simple class to eat return keys. We use it to avoid propagating the return * key from our KLineEdit to a dialog using TagWidget. * We can't use KLineEdit::setTrapReturnKey() because it does not play well * with QCompleter, it only deals with KCompletion. */ class ReturnKeyEater : public QObject { public: explicit ReturnKeyEater(QObject* parent = nullptr) : QObject(parent) {} protected: - bool eventFilter(QObject*, QEvent* event) Q_DECL_OVERRIDE + bool eventFilter(QObject*, QEvent* event) override { if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) { QKeyEvent* keyEvent = static_cast(event); switch (keyEvent->key()) { case Qt::Key_Return: case Qt::Key_Enter: return true; default: return false; } } return false; } }; struct TagWidgetPrivate { TagWidget* q; TagInfo mTagInfo; QListView* mListView; QComboBox* mComboBox; QPushButton* mAddButton; AbstractSemanticInfoBackEnd* mBackEnd; TagCompleterModel* mTagCompleterModel; TagModel* mAssignedTagModel; void setupWidgets() { mListView = new QListView; TagItemDelegate* delegate = new TagItemDelegate(mListView); QObject::connect(delegate, SIGNAL(removeTagRequested(SemanticInfoTag)), q, SLOT(removeTag(SemanticInfoTag))); QObject::connect(delegate, SIGNAL(assignTagToAllRequested(SemanticInfoTag)), q, SLOT(assignTag(SemanticInfoTag))); mListView->setItemDelegate(delegate); mListView->setModel(mAssignedTagModel); mComboBox = new QComboBox; mComboBox->setEditable(true); mComboBox->setInsertPolicy(QComboBox::NoInsert); mTagCompleterModel = new TagCompleterModel(q); QCompleter* completer = new QCompleter(q); completer->setCaseSensitivity(Qt::CaseInsensitive); completer->setModel(mTagCompleterModel); mComboBox->setCompleter(completer); mComboBox->setModel(mTagCompleterModel); mAddButton = new QPushButton; mAddButton->setIcon(QIcon::fromTheme("list-add")); mAddButton->setToolTip(i18n("Add tag")); mAddButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); QObject::connect(mAddButton, SIGNAL(clicked()), q, SLOT(addTagFromComboBox())); QVBoxLayout* layout = new QVBoxLayout(q); layout->setMargin(0); layout->addWidget(mListView); QHBoxLayout* hLayout = new QHBoxLayout; hLayout->addWidget(mComboBox); hLayout->addWidget(mAddButton); layout->addLayout(hLayout); q->setTabOrder(mComboBox, mListView); } void fillTagModel() { Q_ASSERT(mBackEnd); mAssignedTagModel->clear(); TagInfo::ConstIterator it = mTagInfo.constBegin(), end = mTagInfo.constEnd(); for (; it != end; ++it) { mAssignedTagModel->addTag( it.key(), QString(), it.value() ? TagModel::FullyAssigned : TagModel::PartiallyAssigned); } } void updateCompleterModel() { mTagCompleterModel->setTagInfo(mTagInfo); } }; TagWidget::TagWidget(QWidget* parent) : QWidget(parent) , d(new TagWidgetPrivate) { d->q = this; d->mBackEnd = nullptr; d->mAssignedTagModel = new TagModel(this); d->setupWidgets(); installEventFilter(new ReturnKeyEater(this)); connect(d->mComboBox->lineEdit(), SIGNAL(returnPressed()), SLOT(addTagFromComboBox())); } TagWidget::~TagWidget() { delete d; } void TagWidget::setSemanticInfoBackEnd(AbstractSemanticInfoBackEnd* backEnd) { d->mBackEnd = backEnd; d->mAssignedTagModel->setSemanticInfoBackEnd(backEnd); d->mTagCompleterModel->setSemanticInfoBackEnd(backEnd); } void TagWidget::setTagInfo(const TagInfo& tagInfo) { d->mTagInfo = tagInfo; d->fillTagModel(); d->updateCompleterModel(); } void TagWidget::addTagFromComboBox() { Q_ASSERT(d->mBackEnd); QString label = d->mComboBox->currentText(); if (label.isEmpty()) { return; } assignTag(d->mBackEnd->tagForLabel(label.trimmed())); // Use a QTimer because if the tag is new, it will be inserted in the model // and QComboBox will sometimes select it. At least it does so when the // model is empty. QTimer::singleShot(0, d->mComboBox, SLOT(clearEditText())); } void TagWidget::assignTag(const SemanticInfoTag& tag) { d->mTagInfo[tag] = true; d->mAssignedTagModel->addTag(tag); d->updateCompleterModel(); emit tagAssigned(tag); } void TagWidget::removeTag(const SemanticInfoTag& tag) { d->mTagInfo.remove(tag); d->mAssignedTagModel->removeTag(tag); d->updateCompleterModel(); emit tagRemoved(tag); } } // namespace diff --git a/lib/shadowfilter.h b/lib/shadowfilter.h index f6a25795..50aa6a32 100644 --- a/lib/shadowfilter.h +++ b/lib/shadowfilter.h @@ -1,68 +1,68 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2011 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, Cambridge, MA 02110-1301, USA. */ #ifndef SHADOWFILTER_H #define SHADOWFILTER_H #include // Local // KDE // Qt #include class QColor; class QWidget; namespace Gwenview { struct ShadowFilterPrivate; /** * Paint shadows on widget edges */ class GWENVIEWLIB_EXPORT ShadowFilter : public QObject { Q_OBJECT public: enum WidgetEdge { LeftEdge, TopEdge, RightEdge, BottomEdge }; explicit ShadowFilter(QWidget* parent); - ~ShadowFilter() Q_DECL_OVERRIDE; + ~ShadowFilter() override; void setShadow(WidgetEdge edge, const QColor& color); void reset(); protected: - bool eventFilter(QObject*, QEvent*) Q_DECL_OVERRIDE; + bool eventFilter(QObject*, QEvent*) override; private: ShadowFilterPrivate* const d; }; } // namespace #endif /* SHADOWFILTER_H */ diff --git a/lib/slidecontainer.h b/lib/slidecontainer.h index 1a752f77..a1dea292 100644 --- a/lib/slidecontainer.h +++ b/lib/slidecontainer.h @@ -1,101 +1,101 @@ /* 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. */ #ifndef SLIDECONTAINER_H #define SLIDECONTAINER_H // Qt #include #include #include #include class QPropertyAnimation; namespace Gwenview { /** * This widget is design to contain one child widget, the "content" widget. * It will start hidden by default. Calling slideIn() will slide in the content * widget from the top border. Calling slideOut() will slide it out. */ class GWENVIEWLIB_EXPORT SlideContainer : public QFrame { Q_OBJECT Q_PROPERTY(int slideHeight READ slideHeight WRITE setSlideHeight) public: explicit SlideContainer(QWidget* parent = nullptr); /** * Returns the content widget */ QWidget* content() const; /** * Defines the content widget */ void setContent(QWidget* content); - QSize sizeHint() const Q_DECL_OVERRIDE; + QSize sizeHint() const override; - QSize minimumSizeHint() const Q_DECL_OVERRIDE; + QSize minimumSizeHint() const override; int slideHeight() const; Q_INVOKABLE void setSlideHeight(int height); public Q_SLOTS: /** * Slides the content widget in. * Calling it multiple times won't cause the animation to be replayed. */ void slideIn(); /** * Slides the content widget out. * Calling it multiple times won't cause the animation to be replayed. */ void slideOut(); Q_SIGNALS: void slidedIn(); void slidedOut(); protected: - void resizeEvent(QResizeEvent*) Q_DECL_OVERRIDE; - bool eventFilter(QObject*, QEvent* event) Q_DECL_OVERRIDE; + void resizeEvent(QResizeEvent*) override; + bool eventFilter(QObject*, QEvent* event) override; private Q_SLOTS: void slotAnimFinished(); private: QPointer mContent; QPointer mAnim; bool mSlidingOut; void adjustContentGeometry(); void animTo(int height); }; } /* namespace */ #endif /* SLIDECONTAINER_H */ diff --git a/lib/statusbartoolbutton.h b/lib/statusbartoolbutton.h index aa2b8a5a..ba8e62f3 100644 --- a/lib/statusbartoolbutton.h +++ b/lib/statusbartoolbutton.h @@ -1,62 +1,62 @@ // 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. */ #ifndef STATUSBARTOOLBUTTON_H #define STATUSBARTOOLBUTTON_H #include // Qt #include namespace Gwenview { /** * A thin tool button which can be grouped with another and look like one solid * bar: * * ( button1 | button2 ) */ class GWENVIEWLIB_EXPORT StatusBarToolButton : public QToolButton { Q_OBJECT public: enum GroupPosition { NotGrouped = 0, GroupLeft = 1, GroupRight = 2, GroupCenter = 3 }; explicit StatusBarToolButton(QWidget* parent = nullptr); void setGroupPosition(StatusBarToolButton::GroupPosition groupPosition); protected: - void paintEvent(QPaintEvent* event) Q_DECL_OVERRIDE; + void paintEvent(QPaintEvent* event) override; private: GroupPosition mGroupPosition; }; } // namespace #endif /* STATUSBARTOOLBUTTON_H */ diff --git a/lib/stylesheetutils.cpp b/lib/stylesheetutils.cpp index dabeea3f..67920f26 100644 --- a/lib/stylesheetutils.cpp +++ b/lib/stylesheetutils.cpp @@ -1,69 +1,69 @@ /* Gwenview: an image viewer Copyright 2007 Aurélien Gâteau Copyright 2018 Huon Imberger 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. */ #include "stylesheetutils.h" // Qt // KDE // Local #include "paintutils.h" namespace Gwenview { namespace StyleSheetUtils { QString rgba(const QColor &color) { return QString::fromLocal8Bit("rgba(%1, %2, %3, %4)") .arg(color.red()) .arg(color.green()) .arg(color.blue()) .arg(color.alpha()); } QString gradient(Qt::Orientation orientation, const QColor &color, int value) { int x2, y2; if (orientation == Qt::Horizontal) { x2 = 0; y2 = 1; } else { x2 = 1; y2 = 0; } QString grad = - "qlineargradient(x1:0, y1:0, x2:%1, y2:%2," - "stop:0 %3, stop: 1 %4)"; + QStringLiteral("qlineargradient(x1:0, y1:0, x2:%1, y2:%2," + "stop:0 %3, stop: 1 %4)"); return grad .arg(x2) .arg(y2) .arg(rgba(PaintUtils::adjustedHsv(color, 0, 0, qMin(255 - color.value(), value / 2)))) .arg(rgba(PaintUtils::adjustedHsv(color, 0, 0, -qMin(color.value(), value / 2)))) ; } } // namespace } // namespace diff --git a/lib/thumbnailprovider/thumbnailgenerator.cpp b/lib/thumbnailprovider/thumbnailgenerator.cpp index 193ab236..6337de11 100644 --- a/lib/thumbnailprovider/thumbnailgenerator.cpp +++ b/lib/thumbnailprovider/thumbnailgenerator.cpp @@ -1,322 +1,322 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2012 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, Cambridge, MA 02110-1301, USA. */ // Self #include "thumbnailgenerator.h" // Local #include "imageutils.h" #include "jpegcontent.h" #include "gwenviewconfig.h" #include "exiv2imageloader.h" // KDE #include #ifdef KDCRAW_FOUND #include #endif // Qt #include #include #include namespace Gwenview { #undef ENABLE_LOG #undef LOG //#define ENABLE_LOG #ifdef ENABLE_LOG #define LOG(x) //qDebug() << x #else #define LOG(x) ; #endif const int MIN_PREV_SIZE = 1000; //------------------------------------------------------------------------ // // ThumbnailContext // //------------------------------------------------------------------------ bool ThumbnailContext::load(const QString &pixPath, int pixelSize) { mImage = QImage(); mNeedCaching = true; Orientation orientation = NORMAL; QImage originalImage; QSize originalSize; - QByteArray formatHint = pixPath.section('.', -1).toLocal8Bit().toLower(); + QByteArray formatHint = pixPath.section(QLatin1Char('.'), -1).toLocal8Bit().toLower(); QImageReader reader(pixPath); JpegContent content; QByteArray format; QByteArray data; QBuffer buffer; int previewRatio = 1; #ifdef KDCRAW_FOUND // raw images deserve special treatment - if (KDcrawIface::KDcraw::rawFilesList().contains(QString(formatHint))) { + if (KDcrawIface::KDcraw::rawFilesList().contains(QString::fromLatin1(formatHint))) { // use KDCraw to extract the preview bool ret = KDcrawIface::KDcraw::loadEmbeddedPreview(data, pixPath); // We need QImage. Loading JpegContent from QImage - exif lost // Loading QImage from JpegContent - unimplemented, would go with loadFromData if (!ret || !originalImage.loadFromData(data) || qMin(originalImage.width(), originalImage.height()) < MIN_PREV_SIZE) { // if the emebedded preview loading failed or gets just a small image, load // half preview instead. That's slower... if (!KDcrawIface::KDcraw::loadHalfPreview(data, pixPath)) { qWarning() << "unable to get preview for " << pixPath.toUtf8().constData(); return false; } previewRatio = 2; } // And we need JpegContent too because of EXIF (orientation!). if (!content.loadFromData(data)) { qWarning() << "unable to load preview for " << pixPath.toUtf8().constData(); return false; } buffer.setBuffer(&data); buffer.open(QIODevice::ReadOnly); reader.setDevice(&buffer); reader.setFormat(formatHint); } else { #else { #endif if (!reader.canRead()) { reader.setDecideFormatFromContent(true); // Set filename again, otherwise QImageReader won't restart from scratch reader.setFileName(pixPath); } if (reader.format() == "jpeg" && GwenviewConfig::applyExifOrientation()) { content.load(pixPath); } } // If there's jpeg content (from jpg or raw files), try to load an embedded thumbnail, if available. // If applyExifOrientation is not set, don't use the // embedded thumbnail since it might be rotated differently // than the actual image if (!content.rawData().isEmpty() && GwenviewConfig::applyExifOrientation()) { QImage thumbnail = content.thumbnail(); orientation = content.orientation(); if (qMax(thumbnail.width(), thumbnail.height()) >= pixelSize) { mImage = thumbnail; if (orientation != NORMAL && orientation != NOT_AVAILABLE) { QMatrix matrix = ImageUtils::transformMatrix(orientation); mImage = mImage.transformed(matrix); } mOriginalWidth = content.size().width(); mOriginalHeight = content.size().height(); return true; } } // Generate thumbnail from full image originalSize = reader.size(); if (originalSize.isValid() && reader.supportsOption(QImageIOHandler::ScaledSize) && qMax(originalSize.width(), originalSize.height()) >= pixelSize) { QSizeF scaledSize = originalSize; scaledSize.scale(pixelSize, pixelSize, Qt::KeepAspectRatio); if (!scaledSize.isEmpty()) { reader.setScaledSize(scaledSize.toSize()); } } // format() is empty after QImageReader::read() is called format = reader.format(); if (!reader.read(&originalImage)) { return false; } if (!originalSize.isValid()) { originalSize = originalImage.size(); } mOriginalWidth = originalSize.width() * previewRatio; mOriginalHeight = originalSize.height() * previewRatio; if (qMax(mOriginalWidth, mOriginalHeight) <= pixelSize) { mImage = originalImage; mNeedCaching = format != "png"; } else { mImage = originalImage.scaled(pixelSize, pixelSize, Qt::KeepAspectRatio); } // Rotate if necessary if (orientation != NORMAL && orientation != NOT_AVAILABLE && GwenviewConfig::applyExifOrientation()) { QMatrix matrix = ImageUtils::transformMatrix(orientation); mImage = mImage.transformed(matrix); switch (orientation) { case TRANSPOSE: case ROT_90: case TRANSVERSE: case ROT_270: qSwap(mOriginalWidth, mOriginalHeight); break; default: break; } } return true; } //------------------------------------------------------------------------ // // ThumbnailGenerator // //------------------------------------------------------------------------ ThumbnailGenerator::ThumbnailGenerator() : mCancel(false) {} void ThumbnailGenerator::load( const QString& originalUri, time_t originalTime, KIO::filesize_t originalFileSize, const QString& originalMimeType, const QString& pixPath, const QString& thumbnailPath, ThumbnailGroup::Enum group) { QMutexLocker lock(&mMutex); Q_ASSERT(mPixPath.isNull()); mOriginalUri = originalUri; mOriginalTime = originalTime; mOriginalFileSize = originalFileSize; mOriginalMimeType = originalMimeType; mPixPath = pixPath; mThumbnailPath = thumbnailPath; mThumbnailGroup = group; if (!isRunning()) start(); mCond.wakeOne(); } QString ThumbnailGenerator::originalUri() const { return mOriginalUri; } time_t ThumbnailGenerator::originalTime() const { return mOriginalTime; } KIO::filesize_t ThumbnailGenerator::originalFileSize() const { return mOriginalFileSize; } QString ThumbnailGenerator::originalMimeType() const { return mOriginalMimeType; } bool ThumbnailGenerator::testCancel() { QMutexLocker lock(&mMutex); return mCancel; } void ThumbnailGenerator::cancel() { QMutexLocker lock(&mMutex); mCancel = true; mCond.wakeOne(); } void ThumbnailGenerator::run() { LOG(""); while (!testCancel()) { QString pixPath; int pixelSize; { QMutexLocker lock(&mMutex); // empty mPixPath means nothing to do LOG("Waiting for mPixPath"); if (mPixPath.isNull()) { LOG("mPixPath.isNull"); mCond.wait(&mMutex); } } if (testCancel()) { return; } { QMutexLocker lock(&mMutex); pixPath = mPixPath; pixelSize = ThumbnailGroup::pixelSize(mThumbnailGroup); } Q_ASSERT(!pixPath.isNull()); LOG("Loading" << pixPath); ThumbnailContext context; bool ok = context.load(pixPath, pixelSize); { QMutexLocker lock(&mMutex); if (ok) { mImage = context.mImage; mOriginalWidth = context.mOriginalWidth; mOriginalHeight = context.mOriginalHeight; if (context.mNeedCaching) { cacheThumbnail(); } } else { qWarning() << "Could not generate thumbnail for file" << mOriginalUri; } mPixPath.clear(); // done, ready for next } if (testCancel()) { return; } { QSize size(mOriginalWidth, mOriginalHeight); LOG("emitting done signal, size=" << size); QMutexLocker lock(&mMutex); done(mImage, size); LOG("Done"); } } LOG("Ending thread"); } void ThumbnailGenerator::cacheThumbnail() { - mImage.setText("Thumb::URI" , mOriginalUri); - mImage.setText("Thumb::MTime" , QString::number(mOriginalTime)); - mImage.setText("Thumb::Size" , QString::number(mOriginalFileSize)); - mImage.setText("Thumb::Mimetype" , mOriginalMimeType); - mImage.setText("Thumb::Image::Width" , QString::number(mOriginalWidth)); - mImage.setText("Thumb::Image::Height", QString::number(mOriginalHeight)); - mImage.setText("Software" , QStringLiteral("Gwenview")); + mImage.setText(QStringLiteral("Thumb::URI") , mOriginalUri); + mImage.setText(QStringLiteral("Thumb::MTime") , QString::number(mOriginalTime)); + mImage.setText(QStringLiteral("Thumb::Size") , QString::number(mOriginalFileSize)); + mImage.setText(QStringLiteral("Thumb::Mimetype") , mOriginalMimeType); + mImage.setText(QStringLiteral("Thumb::Image::Width") , QString::number(mOriginalWidth)); + mImage.setText(QStringLiteral("Thumb::Image::Height"), QString::number(mOriginalHeight)); + mImage.setText(QStringLiteral("Software") , QStringLiteral("Gwenview")); emit thumbnailReadyToBeCached(mThumbnailPath, mImage); } } // namespace diff --git a/lib/thumbnailprovider/thumbnailgenerator.h b/lib/thumbnailprovider/thumbnailgenerator.h index 9313e62f..af877763 100644 --- a/lib/thumbnailprovider/thumbnailgenerator.h +++ b/lib/thumbnailprovider/thumbnailgenerator.h @@ -1,96 +1,96 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2012 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, Cambridge, MA 02110-1301, USA. */ #ifndef THUMBNAILGENERATOR_H #define THUMBNAILGENERATOR_H // Local #include // KDE #include // Qt #include #include #include #include namespace Gwenview { struct ThumbnailContext { QImage mImage; int mOriginalWidth; int mOriginalHeight; bool mNeedCaching; bool load(const QString &pixPath, int pixelSize); }; class ThumbnailGenerator : public QThread { Q_OBJECT public: ThumbnailGenerator(); void load( const QString& originalUri, time_t originalTime, KIO::filesize_t originalFileSize, const QString& originalMimeType, const QString& pixPath, const QString& thumbnailPath, ThumbnailGroup::Enum group); void cancel(); QString originalUri() const; time_t originalTime() const; KIO::filesize_t originalFileSize() const; QString originalMimeType() const; protected: - void run() Q_DECL_OVERRIDE; + void run() override; Q_SIGNALS: void done(const QImage&, const QSize&); void thumbnailReadyToBeCached(const QString& thumbnailPath, const QImage&); private: bool testCancel(); void cacheThumbnail(); QImage mImage; QString mPixPath; QString mThumbnailPath; QString mOriginalUri; time_t mOriginalTime; KIO::filesize_t mOriginalFileSize; QString mOriginalMimeType; int mOriginalWidth; int mOriginalHeight; QMutex mMutex; QWaitCondition mCond; ThumbnailGroup::Enum mThumbnailGroup; bool mCancel; }; } // namespace #endif /* THUMBNAILGENERATOR_H */ diff --git a/lib/thumbnailprovider/thumbnailprovider.cpp b/lib/thumbnailprovider/thumbnailprovider.cpp index 6624bc29..df41cb28 100644 --- a/lib/thumbnailprovider/thumbnailprovider.cpp +++ b/lib/thumbnailprovider/thumbnailprovider.cpp @@ -1,569 +1,569 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview - A simple image viewer for KDE Copyright 2000-2007 Aurélien Gâteau This class is based on the ImagePreviewJob class from Konqueror. */ /* This file is part of the KDE project Copyright (C) 2000 David Faure 2000 Carsten Pfeiffer 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. */ #include "thumbnailprovider.h" #include #include #include // Qt #include #include #include #include #include #include #include #include #include // KDE #include #include #include // Local #include "mimetypeutils.h" #include "thumbnailwriter.h" #include "thumbnailgenerator.h" #include "urlutils.h" namespace Gwenview { #undef ENABLE_LOG #undef LOG //#define ENABLE_LOG #ifdef ENABLE_LOG #define LOG(x) qDebug() << x #else #define LOG(x) ; #endif Q_GLOBAL_STATIC(ThumbnailWriter, sThumbnailWriter) static QString generateOriginalUri(const QUrl &url_) { QUrl url = url_; return url.adjusted(QUrl::RemovePassword).url(); } static QString generateThumbnailPath(const QString& uri, ThumbnailGroup::Enum group) { QString baseDir = ThumbnailProvider::thumbnailBaseDir(group); QCryptographicHash md5(QCryptographicHash::Md5); md5.addData(QFile::encodeName(uri)); - return baseDir + QFile::encodeName(md5.result().toHex()) + ".png"; + return baseDir + QFile::encodeName(QString::fromLatin1(md5.result().toHex())) + QStringLiteral(".png"); } //------------------------------------------------------------------------ // // ThumbnailProvider static methods // //------------------------------------------------------------------------ static QString sThumbnailBaseDir; QString ThumbnailProvider::thumbnailBaseDir() { if (sThumbnailBaseDir.isEmpty()) { const QByteArray customDir = qgetenv("GV_THUMBNAIL_DIR"); if (customDir.isEmpty()) { sThumbnailBaseDir = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QStringLiteral("/thumbnails/"); } else { - sThumbnailBaseDir = QFile::decodeName(customDir) + '/'; + sThumbnailBaseDir = QFile::decodeName(customDir) + QLatin1Char('/'); } } return sThumbnailBaseDir; } void ThumbnailProvider::setThumbnailBaseDir(const QString& dir) { sThumbnailBaseDir = dir; } QString ThumbnailProvider::thumbnailBaseDir(ThumbnailGroup::Enum group) { QString dir = thumbnailBaseDir(); switch (group) { case ThumbnailGroup::Normal: - dir += "normal/"; + dir += QStringLiteral("normal/"); break; case ThumbnailGroup::Large: - dir += "large/"; + dir += QStringLiteral("large/"); break; } return dir; } void ThumbnailProvider::deleteImageThumbnail(const QUrl &url) { QString uri = generateOriginalUri(url); QFile::remove(generateThumbnailPath(uri, ThumbnailGroup::Normal)); QFile::remove(generateThumbnailPath(uri, ThumbnailGroup::Large)); } static void moveThumbnailHelper(const QString& oldUri, const QString& newUri, ThumbnailGroup::Enum group) { QString oldPath = generateThumbnailPath(oldUri, group); QString newPath = generateThumbnailPath(newUri, group); QImage thumb; if (!thumb.load(oldPath)) { return; } - thumb.setText("Thumb::URI", newUri); + thumb.setText(QStringLiteral("Thumb::URI"), newUri); thumb.save(newPath, "png"); QFile::remove(QFile::encodeName(oldPath)); } void ThumbnailProvider::moveThumbnail(const QUrl &oldUrl, const QUrl& newUrl) { QString oldUri = generateOriginalUri(oldUrl); QString newUri = generateOriginalUri(newUrl); moveThumbnailHelper(oldUri, newUri, ThumbnailGroup::Normal); moveThumbnailHelper(oldUri, newUri, ThumbnailGroup::Large); } //------------------------------------------------------------------------ // // ThumbnailProvider implementation // //------------------------------------------------------------------------ ThumbnailProvider::ThumbnailProvider() : KIO::Job() , mState(STATE_NEXTTHUMB) , mOriginalTime(0) { LOG(this); // Make sure we have a place to store our thumbnails QString thumbnailDirNormal = ThumbnailProvider::thumbnailBaseDir(ThumbnailGroup::Normal); QString thumbnailDirLarge = ThumbnailProvider::thumbnailBaseDir(ThumbnailGroup::Large); QDir().mkpath(thumbnailDirNormal); QDir().mkpath(thumbnailDirLarge); QFile::setPermissions(thumbnailDirNormal, QFileDevice::WriteOwner | QFileDevice::ReadOwner | QFileDevice::ExeOwner); QFile::setPermissions(thumbnailDirLarge, QFileDevice::WriteOwner | QFileDevice::ReadOwner | QFileDevice::ExeOwner); // Look for images and store the items in our todo list mCurrentItem = KFileItem(); mThumbnailGroup = ThumbnailGroup::Large; createNewThumbnailGenerator(); } ThumbnailProvider::~ThumbnailProvider() { LOG(this); abortSubjob(); mThumbnailGenerator->cancel(); disconnect(mThumbnailGenerator, nullptr, this, nullptr); disconnect(mThumbnailGenerator, nullptr, sThumbnailWriter, nullptr); connect(mThumbnailGenerator, SIGNAL(finished()), mThumbnailGenerator, SLOT(deleteLater())); if (mPreviousThumbnailGenerator) { disconnect(mPreviousThumbnailGenerator, nullptr, sThumbnailWriter, nullptr); } sThumbnailWriter->wait(); } void ThumbnailProvider::stop() { // Clear mItems and create a new ThumbnailGenerator if mThumbnailGenerator is running, // but also make sure that at most two ThumbnailGenerators are running. // startCreatingThumbnail() will take care that these two threads won't work on the same item. mItems.clear(); abortSubjob(); if (mThumbnailGenerator->isRunning() && !mPreviousThumbnailGenerator) { mPreviousThumbnailGenerator = mThumbnailGenerator; mPreviousThumbnailGenerator->cancel(); disconnect(mPreviousThumbnailGenerator, nullptr, this, nullptr); connect(mPreviousThumbnailGenerator, SIGNAL(finished()), mPreviousThumbnailGenerator, SLOT(deleteLater())); createNewThumbnailGenerator(); mCurrentItem = KFileItem(); } } const KFileItemList& ThumbnailProvider::pendingItems() const { return mItems; } void ThumbnailProvider::setThumbnailGroup(ThumbnailGroup::Enum group) { mThumbnailGroup = group; } void ThumbnailProvider::appendItems(const KFileItemList& items) { if (!mItems.isEmpty()) { QSet itemSet; Q_FOREACH(const KFileItem & item, mItems) { itemSet.insert(item.url().url()); } Q_FOREACH(const KFileItem & item, items) { if (!itemSet.contains(item.url().url())) { mItems.append(item); } } } else { mItems = items; } if (mCurrentItem.isNull()) { determineNextIcon(); } } void ThumbnailProvider::removeItems(const KFileItemList& itemList) { if (mItems.isEmpty()) { return; } Q_FOREACH(const KFileItem & item, itemList) { // If we are removing the next item, update to be the item after or the // first if we removed the last item mItems.removeAll(item); if (item == mCurrentItem) { abortSubjob(); } } // No more current item, carry on to the next remaining item if (mCurrentItem.isNull()) { determineNextIcon(); } } void ThumbnailProvider::removePendingItems() { mItems.clear(); } bool ThumbnailProvider::isRunning() const { return !mCurrentItem.isNull(); } //-Internal-------------------------------------------------------------- void ThumbnailProvider::createNewThumbnailGenerator() { mThumbnailGenerator = new ThumbnailGenerator; connect(mThumbnailGenerator, SIGNAL(done(QImage,QSize)), SLOT(thumbnailReady(QImage,QSize)), Qt::QueuedConnection); connect(mThumbnailGenerator, SIGNAL(thumbnailReadyToBeCached(QString,QImage)), sThumbnailWriter, SLOT(queueThumbnail(QString,QImage)), Qt::QueuedConnection); } void ThumbnailProvider::abortSubjob() { if (hasSubjobs()) { LOG("Killing subjob"); KJob* job = subjobs().first(); job->kill(); removeSubjob(job); mCurrentItem = KFileItem(); } } void ThumbnailProvider::determineNextIcon() { LOG(this); mState = STATE_NEXTTHUMB; // No more items ? if (mItems.isEmpty()) { LOG("No more items. Nothing to do"); mCurrentItem = KFileItem(); finished(); return; } mCurrentItem = mItems.takeFirst(); LOG("mCurrentItem.url=" << mCurrentItem.url()); // First, stat the orig file mState = STATE_STATORIG; mCurrentUrl = mCurrentItem.url().adjusted(QUrl::NormalizePathSegments); mOriginalFileSize = mCurrentItem.size(); // Do direct stat instead of using KIO if the file is local (faster) if (UrlUtils::urlIsFastLocalFile(mCurrentUrl)) { QFileInfo fileInfo(mCurrentUrl.toLocalFile()); mOriginalTime = fileInfo.lastModified().toTime_t(); QMetaObject::invokeMethod(this, "checkThumbnail", Qt::QueuedConnection); } else { KIO::Job* job = KIO::stat(mCurrentUrl, KIO::HideProgressInfo); KJobWidgets::setWindow(job, qApp->activeWindow()); LOG("KIO::stat orig" << mCurrentUrl.url()); addSubjob(job); } LOG("/determineNextIcon" << this); } void ThumbnailProvider::slotResult(KJob * job) { LOG(mState); removeSubjob(job); Q_ASSERT(subjobs().isEmpty()); // We should have only one job at a time switch (mState) { case STATE_NEXTTHUMB: Q_ASSERT(false); determineNextIcon(); return; case STATE_STATORIG: { // Could not stat original, drop this one and move on to the next one if (job->error()) { emitThumbnailLoadingFailed(); determineNextIcon(); return; } // Get modification time of the original file KIO::UDSEntry entry = static_cast(job)->statResult(); mOriginalTime = entry.numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME, -1); checkThumbnail(); return; } case STATE_DOWNLOADORIG: if (job->error()) { emitThumbnailLoadingFailed(); LOG("Delete temp file" << mTempPath); QFile::remove(mTempPath); mTempPath.clear(); determineNextIcon(); } else { startCreatingThumbnail(mTempPath); } return; case STATE_PREVIEWJOB: determineNextIcon(); return; } } void ThumbnailProvider::thumbnailReady(const QImage& _img, const QSize& _size) { QImage img = _img; QSize size = _size; if (!img.isNull()) { emitThumbnailLoaded(img, size); } else { emitThumbnailLoadingFailed(); } if (!mTempPath.isEmpty()) { LOG("Delete temp file" << mTempPath); QFile::remove(mTempPath); mTempPath.clear(); } determineNextIcon(); } QImage ThumbnailProvider::loadThumbnailFromCache() const { QImage image = sThumbnailWriter->value(mThumbnailPath); if (!image.isNull()) { return image; } image = QImage(mThumbnailPath); if (image.isNull() && mThumbnailGroup == ThumbnailGroup::Normal) { // If there is a large-sized thumbnail, generate the normal-sized version from it QString largeThumbnailPath = generateThumbnailPath(mOriginalUri, ThumbnailGroup::Large); QImage largeImage(largeThumbnailPath); if (largeImage.isNull()) { return image; } int size = ThumbnailGroup::pixelSize(ThumbnailGroup::Normal); image = largeImage.scaled(size, size, Qt::KeepAspectRatio, Qt::SmoothTransformation); Q_FOREACH(const QString& key, largeImage.textKeys()) { QString text = largeImage.text(key); image.setText(key, text); } sThumbnailWriter->queueThumbnail(mThumbnailPath, image); } return image; } void ThumbnailProvider::checkThumbnail() { if (mCurrentItem.isNull()) { // This can happen if current item has been removed by removeItems() determineNextIcon(); return; } // If we are in the thumbnail dir, just load the file if (mCurrentUrl.isLocalFile() && mCurrentUrl.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).path().startsWith(thumbnailBaseDir())) { QImage image(mCurrentUrl.toLocalFile()); emitThumbnailLoaded(image, image.size()); determineNextIcon(); return; } mOriginalUri = generateOriginalUri(mCurrentUrl); mThumbnailPath = generateThumbnailPath(mOriginalUri, mThumbnailGroup); LOG("Stat thumb" << mThumbnailPath); QImage thumb = loadThumbnailFromCache(); - KIO::filesize_t fileSize = thumb.text("Thumb::Size").toULongLong(); + KIO::filesize_t fileSize = thumb.text(QStringLiteral("Thumb::Size")).toULongLong(); if (!thumb.isNull()) { - if (thumb.text("Thumb::URI") == mOriginalUri && - thumb.text("Thumb::MTime").toInt() == mOriginalTime && + if (thumb.text(QStringLiteral("Thumb::URI")) == mOriginalUri && + thumb.text(QStringLiteral("Thumb::MTime")).toInt() == mOriginalTime && (fileSize == 0 || fileSize == mOriginalFileSize)) { int width = 0, height = 0; QSize size; bool ok; - width = thumb.text("Thumb::Image::Width").toInt(&ok); - if (ok) height = thumb.text("Thumb::Image::Height").toInt(&ok); + width = thumb.text(QStringLiteral("Thumb::Image::Width")).toInt(&ok); + if (ok) height = thumb.text(QStringLiteral("Thumb::Image::Height")).toInt(&ok); if (ok) { size = QSize(width, height); } else { LOG("Thumbnail for" << mOriginalUri << "does not contain correct image size information"); // Don't try to determine the size of a video, it probably won't work and // will cause high I/O usage with big files (bug #307007). if (MimeTypeUtils::urlKind(mCurrentUrl) == MimeTypeUtils::KIND_VIDEO) { emitThumbnailLoaded(thumb, QSize()); determineNextIcon(); return; } } emitThumbnailLoaded(thumb, size); determineNextIcon(); return; } } // Thumbnail not found or not valid if (MimeTypeUtils::fileItemKind(mCurrentItem) == MimeTypeUtils::KIND_RASTER_IMAGE) { if (mCurrentUrl.isLocalFile()) { // Original is a local file, create the thumbnail startCreatingThumbnail(mCurrentUrl.toLocalFile()); } else { // Original is remote, download it mState = STATE_DOWNLOADORIG; QTemporaryFile tempFile; tempFile.setAutoRemove(false); if (!tempFile.open()) { qWarning() << "Couldn't create temp file to download " << mCurrentUrl.toDisplayString(); emitThumbnailLoadingFailed(); determineNextIcon(); return; } mTempPath = tempFile.fileName(); QUrl url = QUrl::fromLocalFile(mTempPath); KIO::Job* job = KIO::file_copy(mCurrentUrl, url, -1, KIO::Overwrite | KIO::HideProgressInfo); KJobWidgets::setWindow(job, qApp->activeWindow()); LOG("Download remote file" << mCurrentUrl.toDisplayString() << "to" << url.toDisplayString()); addSubjob(job); } } else { // Not a raster image, use a KPreviewJob LOG("Starting a KPreviewJob for" << mCurrentItem.url()); mState = STATE_PREVIEWJOB; KFileItemList list; list.append(mCurrentItem); const int pixelSize = ThumbnailGroup::pixelSize(mThumbnailGroup); if (mPreviewPlugins.isEmpty()) { mPreviewPlugins = KIO::PreviewJob::availablePlugins(); } KIO::Job* job = KIO::filePreview(list, QSize(pixelSize, pixelSize), &mPreviewPlugins); //KJobWidgets::setWindow(job, qApp->activeWindow()); connect(job, SIGNAL(gotPreview(KFileItem,QPixmap)), this, SLOT(slotGotPreview(KFileItem,QPixmap))); connect(job, SIGNAL(failed(KFileItem)), this, SLOT(emitThumbnailLoadingFailed())); addSubjob(job); } } void ThumbnailProvider::startCreatingThumbnail(const QString& pixPath) { LOG("Creating thumbnail from" << pixPath); // If mPreviousThumbnailGenerator is already working on our current item // its thumbnail will be passed to sThumbnailWriter when ready. So we // connect mPreviousThumbnailGenerator's signal "finished" to determineNextIcon // which will load the thumbnail from sThumbnailWriter or from disk // (because we re-add mCurrentItem to mItems). if (mPreviousThumbnailGenerator && mPreviousThumbnailGenerator->isRunning() && mOriginalUri == mPreviousThumbnailGenerator->originalUri() && mOriginalTime == mPreviousThumbnailGenerator->originalTime() && mOriginalFileSize == mPreviousThumbnailGenerator->originalFileSize() && mCurrentItem.mimetype() == mPreviousThumbnailGenerator->originalMimeType()) { connect(mPreviousThumbnailGenerator, SIGNAL(finished()), SLOT(determineNextIcon())); mItems.prepend(mCurrentItem); return; } mThumbnailGenerator->load(mOriginalUri, mOriginalTime, mOriginalFileSize, mCurrentItem.mimetype(), pixPath, mThumbnailPath, mThumbnailGroup); } void ThumbnailProvider::slotGotPreview(const KFileItem& item, const QPixmap& pixmap) { if (mCurrentItem.isNull()) { // This can happen if current item has been removed by removeItems() return; } LOG(mCurrentItem.url()); QSize size; emit thumbnailLoaded(item, pixmap, size, mOriginalFileSize); } void ThumbnailProvider::emitThumbnailLoaded(const QImage& img, const QSize& size) { if (mCurrentItem.isNull()) { // This can happen if current item has been removed by removeItems() return; } LOG(mCurrentItem.url()); QPixmap thumb = QPixmap::fromImage(img); emit thumbnailLoaded(mCurrentItem, thumb, size, mOriginalFileSize); } void ThumbnailProvider::emitThumbnailLoadingFailed() { if (mCurrentItem.isNull()) { // This can happen if current item has been removed by removeItems() return; } LOG(mCurrentItem.url()); emit thumbnailLoadingFailed(mCurrentItem); } bool ThumbnailProvider::isThumbnailWriterEmpty() { return sThumbnailWriter->isEmpty(); } } // namespace diff --git a/lib/thumbnailprovider/thumbnailprovider.h b/lib/thumbnailprovider/thumbnailprovider.h index 742dbb05..fedd1c3e 100644 --- a/lib/thumbnailprovider/thumbnailprovider.h +++ b/lib/thumbnailprovider/thumbnailprovider.h @@ -1,182 +1,182 @@ // vim: set tabstop=4 shiftwidth=4 expandtab /* Gwenview - A simple image viewer for KDE Copyright 2000-2004 Aurélien Gâteau This class is based on the ImagePreviewJob class from Konqueror. */ /* This file is part of the KDE project Copyright (C) 2000 David Faure 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. */ #ifndef THUMBNAILPROVIDER_H #define THUMBNAILPROVIDER_H #include // Qt #include #include #include // KDE #include #include // Local #include namespace Gwenview { class ThumbnailGenerator; class ThumbnailWriter; /** * A job that determines the thumbnails for the images in the current directory */ class GWENVIEWLIB_EXPORT ThumbnailProvider : public KIO::Job { Q_OBJECT public: ThumbnailProvider(); - ~ThumbnailProvider() Q_DECL_OVERRIDE; + ~ThumbnailProvider() override; void stop(); /** * To be called whenever items are removed from the view */ void removeItems(const KFileItemList& itemList); /** * Remove all pending items */ void removePendingItems(); /** * Returns the list of items waiting for a thumbnail */ const KFileItemList& pendingItems() const; /** * Add items to the job */ void appendItems(const KFileItemList& items); /** * Defines size of thumbnails to generate */ void setThumbnailGroup(ThumbnailGroup::Enum); bool isRunning() const; /** * Returns the thumbnail base dir, independent of the thumbnail size */ static QString thumbnailBaseDir(); /** * Sets the thumbnail base dir, useful for unit-testing */ static void setThumbnailBaseDir(const QString&); /** * Returns the thumbnail base dir, for the @p group */ static QString thumbnailBaseDir(ThumbnailGroup::Enum group); /** * Delete the thumbnail for the @p url */ static void deleteImageThumbnail(const QUrl &url); /** * Move a thumbnail to match a file move */ static void moveThumbnail(const QUrl &oldUrl, const QUrl& newUrl); /** * Returns true if all thumbnails have been written to disk. Useful for * unit-testing. */ static bool isThumbnailWriterEmpty(); Q_SIGNALS: /** * Emitted when the thumbnail for the @p item has been loaded */ void thumbnailLoaded(const KFileItem& item, const QPixmap&, const QSize&, qulonglong); void thumbnailLoadingFailed(const KFileItem& item); /** * Queue is empty */ void finished(); protected: - void slotResult(KJob *job) Q_DECL_OVERRIDE; + void slotResult(KJob *job) override; private Q_SLOTS: void determineNextIcon(); void slotGotPreview(const KFileItem&, const QPixmap&); void checkThumbnail(); void thumbnailReady(const QImage&, const QSize&); void emitThumbnailLoadingFailed(); private: enum { STATE_STATORIG, STATE_DOWNLOADORIG, STATE_PREVIEWJOB, STATE_NEXTTHUMB } mState; KFileItemList mItems; KFileItem mCurrentItem; // The Url of the current item (always equivalent to m_items.first()->item()->url()) QUrl mCurrentUrl; // The Uri of the original image (might be different from mCurrentUrl.url()) QString mOriginalUri; // The modification time of the original image time_t mOriginalTime; // The file size of the original image KIO::filesize_t mOriginalFileSize; // The thumbnail path QString mThumbnailPath; // The temporary path for remote urls QString mTempPath; // Thumbnail group ThumbnailGroup::Enum mThumbnailGroup; ThumbnailGenerator* mThumbnailGenerator; QPointer mPreviousThumbnailGenerator; QStringList mPreviewPlugins; void createNewThumbnailGenerator(); void abortSubjob(); void startCreatingThumbnail(const QString& path); void emitThumbnailLoaded(const QImage& img, const QSize& size); QImage loadThumbnailFromCache() const; }; } // namespace #endif /* THUMBNAILPROVIDER_H */ diff --git a/lib/thumbnailprovider/thumbnailwriter.h b/lib/thumbnailprovider/thumbnailwriter.h index d8da5903..3b5f4085 100644 --- a/lib/thumbnailprovider/thumbnailwriter.h +++ b/lib/thumbnailprovider/thumbnailwriter.h @@ -1,64 +1,64 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2012 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, Cambridge, MA 02110-1301, USA. */ #ifndef THUMBNAILWRITER_H #define THUMBNAILWRITER_H // Local // KDE // Qt #include #include #include class QImage; namespace Gwenview { /** * Store thumbnails to disk when done generating them */ class ThumbnailWriter : public QThread { Q_OBJECT public: // Return thumbnail if it has still not been stored QImage value(const QString&) const; bool isEmpty() const; public Q_SLOTS: void queueThumbnail(const QString&, const QImage&); protected: - void run() Q_DECL_OVERRIDE; + void run() override; private: typedef QHash Cache; Cache mCache; mutable QMutex mMutex; }; } // namespace #endif /* THUMBNAILWRITER_H */ diff --git a/lib/thumbnailview/contextbarbutton.h b/lib/thumbnailview/contextbarbutton.h index ff8a7725..676f98c9 100644 --- a/lib/thumbnailview/contextbarbutton.h +++ b/lib/thumbnailview/contextbarbutton.h @@ -1,65 +1,65 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2011 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, Cambridge, MA 02110-1301, USA. */ /* ***************************************************** ****************************************************** **** NOTE: This class is deprecated. Do not use it. ** **** It will be removed in the QT 6 timeframe. ** ****************************************************** ****************************************************** */ #ifndef CONTEXTBARBUTTON_H #define CONTEXTBARBUTTON_H // Local // KDE // Qt #include namespace Gwenview { struct ContextBarButtonPrivate; /** * A button with a special look, appears when hovering over thumbnails */ class ContextBarButton : public QToolButton { Q_OBJECT public: explicit ContextBarButton(const QString& iconName, QWidget* parent = nullptr); - ~ContextBarButton() Q_DECL_OVERRIDE; + ~ContextBarButton() override; protected: - void paintEvent(QPaintEvent*) Q_DECL_OVERRIDE; + void paintEvent(QPaintEvent*) override; private: ContextBarButtonPrivate* const d; }; } // namespace #endif /* CONTEXTBARBUTTON_H */ diff --git a/lib/thumbnailview/itemeditor.h b/lib/thumbnailview/itemeditor.h index df46d157..e7e4e314 100644 --- a/lib/thumbnailview/itemeditor.h +++ b/lib/thumbnailview/itemeditor.h @@ -1,54 +1,54 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2009 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, Cambridge, MA 02110-1301, USA. */ #ifndef ITEMEDITOR_H #define ITEMEDITOR_H // Qt // KDE #include // Local namespace Gwenview { struct ItemEditorPrivate; class ItemEditor : public KLineEdit { Q_OBJECT public: explicit ItemEditor(QWidget* parent = nullptr); - ~ItemEditor() Q_DECL_OVERRIDE; + ~ItemEditor() override; protected: - void showEvent(QShowEvent*) Q_DECL_OVERRIDE; + void showEvent(QShowEvent*) override; private Q_SLOTS: void resizeToContents(); private: ItemEditorPrivate* const d; }; } // namespace #endif /* ITEMEDITOR_H */ diff --git a/lib/thumbnailview/previewitemdelegate.cpp b/lib/thumbnailview/previewitemdelegate.cpp index 5acbbcdc..3b92bea4 100644 --- a/lib/thumbnailview/previewitemdelegate.cpp +++ b/lib/thumbnailview/previewitemdelegate.cpp @@ -1,981 +1,981 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 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, Cambridge, MA 02110-1301, USA. */ // Self #include "previewitemdelegate.h" #include // Qt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // KDE #include #include #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE #include #endif // Local #include "archiveutils.h" #include "itemeditor.h" #include "paintutils.h" #include "thumbnailview.h" #include "timeutils.h" #include "tooltipwidget.h" #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE #include "../semanticinfo/semanticinfodirmodel.h" #endif // Define this to be able to fine tune the rendering of the selection // background through a config file //#define FINETUNE_SELECTION_BACKGROUND #ifdef FINETUNE_SELECTION_BACKGROUND #include #include #endif //#define DEBUG_DRAW_BORDER //#define DEBUG_DRAW_CURRENT namespace Gwenview { /** * Space between the item outer rect and the content, and between the * thumbnail and the caption */ const int ITEM_MARGIN = 5; /** How darker is the border line around selection */ const int SELECTION_BORDER_DARKNESS = 140; const int FOCUS_BORDER_DARKNESS = 200; /** Radius of the selection rounded corners, in pixels */ const int SELECTION_RADIUS = 5; /** Space between the item outer rect and the context bar */ const int CONTEXTBAR_MARGIN = 1; /** How dark is the shadow, 0 is invisible, 255 is as dark as possible */ const int SHADOW_STRENGTH = 128; /** How many pixels around the thumbnail are shadowed */ const int SHADOW_SIZE = 4; static KFileItem fileItemForIndex(const QModelIndex& index) { Q_ASSERT(index.isValid()); QVariant data = index.data(KDirModel::FileItemRole); return qvariant_cast(data); } static QUrl urlForIndex(const QModelIndex& index) { KFileItem item = fileItemForIndex(index); return item.url(); } struct PreviewItemDelegatePrivate { /** * Maps full text to elided text. */ mutable QHash mElidedTextCache; // Key is height * 1000 + width typedef QHash ShadowCache; mutable ShadowCache mShadowCache; PreviewItemDelegate* q; ThumbnailView* mView; QWidget* mContextBar; QToolButton* mSaveButton; QPixmap mSaveButtonPixmap; QToolButton* mToggleSelectionButton; QToolButton* mFullScreenButton; QToolButton* mRotateLeftButton; QToolButton* mRotateRightButton; #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE KRatingPainter mRatingPainter; #endif QPersistentModelIndex mIndexUnderCursor; QSize mThumbnailSize; PreviewItemDelegate::ThumbnailDetails mDetails; PreviewItemDelegate::ContextBarActions mContextBarActions; Qt::TextElideMode mTextElideMode; QPointer mToolTip; QScopedPointer mToolTipAnimation; void initSaveButtonPixmap() { if (!mSaveButtonPixmap.isNull()) { return; } // Necessary otherwise we won't see the save button itself mSaveButton->adjustSize(); mSaveButtonPixmap = QPixmap(mSaveButton->sizeHint()); mSaveButtonPixmap.fill(Qt::transparent); mSaveButton->render(&mSaveButtonPixmap, QPoint(), QRegion(), QWidget::DrawChildren); } void showContextBar(const QRect& rect, const QPixmap& thumbnailPix) { if (mContextBarActions == PreviewItemDelegate::NoAction) { return; } mContextBar->adjustSize(); // Center bar, except if only showing SelectionAction. const int posX = mContextBarActions == PreviewItemDelegate::SelectionAction ? 0 : (rect.width() - mContextBar->width()) / 2; const int posY = qMax(CONTEXTBAR_MARGIN, mThumbnailSize.height() - thumbnailPix.height() - mContextBar->height()); mContextBar->move(rect.topLeft() + QPoint(posX, posY)); mContextBar->show(); } void initToolTip() { mToolTip = new ToolTipWidget(mView->viewport()); mToolTip->setOpacity(0); mToolTip->show(); } bool hoverEventFilter(QHoverEvent* event) { QModelIndex index = mView->indexAt(event->pos()); if (index != mIndexUnderCursor) { updateHoverUi(index); } else { // Same index, nothing to do, but repaint anyway in case we are // over the rating row mView->update(mIndexUnderCursor); } return false; } void updateHoverUi(const QModelIndex& index) { QModelIndex oldIndex = mIndexUnderCursor; mIndexUnderCursor = index; mView->update(oldIndex); if (QApplication::style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, mView)) { mView->setCursor(mIndexUnderCursor.isValid() ? Qt::PointingHandCursor : Qt::ArrowCursor); } if (mIndexUnderCursor.isValid()) { updateToggleSelectionButton(); updateImageButtons(); const QRect rect = mView->visualRect(mIndexUnderCursor); const QPixmap thumbnailPix = mView->thumbnailForIndex(index); showContextBar(rect, thumbnailPix); if (mView->isModified(mIndexUnderCursor)) { showSaveButton(rect); } else { mSaveButton->hide(); } showToolTip(index); mView->update(mIndexUnderCursor); } else { mContextBar->hide(); mSaveButton->hide(); hideToolTip(); } } QRect ratingRectFromIndexRect(const QRect& rect) const { return QRect( rect.left(), rect.bottom() - ratingRowHeight() - ITEM_MARGIN, rect.width(), ratingRowHeight()); } #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE int ratingFromCursorPosition(const QRect& ratingRect) const { const QPoint pos = mView->viewport()->mapFromGlobal(QCursor::pos()); return mRatingPainter.ratingFromPosition(ratingRect, pos); } #endif bool mouseButtonEventFilter(QEvent::Type type) { #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE const QRect rect = ratingRectFromIndexRect(mView->visualRect(mIndexUnderCursor)); const int rating = ratingFromCursorPosition(rect); if (rating == -1) { return false; } if (type == QEvent::MouseButtonRelease) { q->setDocumentRatingRequested(urlForIndex(mIndexUnderCursor) , rating); } return true; #else return false; #endif } QPoint saveButtonPosition(const QRect& itemRect) const { QSize buttonSize = mSaveButton->sizeHint(); int posX = itemRect.right() - buttonSize.width(); int posY = itemRect.top() + mThumbnailSize.height() + 2 * ITEM_MARGIN - buttonSize.height(); return QPoint(posX, posY); } void showSaveButton(const QRect& itemRect) const { mSaveButton->move(saveButtonPosition(itemRect)); mSaveButton->show(); } void drawBackground(QPainter* painter, const QRect& rect, const QColor& bgColor, const QColor& borderColor) const { int bgH, bgS, bgV; int borderH, borderS, borderV, borderMargin; #ifdef FINETUNE_SELECTION_BACKGROUND QSettings settings(QDir::homePath() + "/colors.ini", QSettings::IniFormat); bgH = settings.value("bg/h").toInt(); bgS = settings.value("bg/s").toInt(); bgV = settings.value("bg/v").toInt(); borderH = settings.value("border/h").toInt(); borderS = settings.value("border/s").toInt(); borderV = settings.value("border/v").toInt(); borderMargin = settings.value("border/margin").toInt(); #else bgH = 0; bgS = -20; bgV = 43; borderH = 0; borderS = -100; borderV = 60; borderMargin = 1; #endif painter->setRenderHint(QPainter::Antialiasing); QRectF rectF = QRectF(rect).adjusted(0.5, 0.5, -0.5, -0.5); QPainterPath path = PaintUtils::roundedRectangle(rectF, SELECTION_RADIUS); QLinearGradient gradient(rectF.topLeft(), rectF.bottomLeft()); gradient.setColorAt(0, PaintUtils::adjustedHsv(bgColor, bgH, bgS, bgV)); gradient.setColorAt(1, bgColor); painter->fillPath(path, gradient); painter->setPen(borderColor); painter->drawPath(path); painter->setPen(PaintUtils::adjustedHsv(borderColor, borderH, borderS, borderV)); rectF = rectF.adjusted(borderMargin, borderMargin, -borderMargin, -borderMargin); path = PaintUtils::roundedRectangle(rectF, SELECTION_RADIUS); painter->drawPath(path); } void drawShadow(QPainter* painter, const QRect& rect) const { const QPoint shadowOffset(-SHADOW_SIZE, -SHADOW_SIZE + 1); int key = rect.height() * 1000 + rect.width(); ShadowCache::Iterator it = mShadowCache.find(key); if (it == mShadowCache.end()) { QSize size = QSize(rect.width() + 2 * SHADOW_SIZE, rect.height() + 2 * SHADOW_SIZE); QColor color(0, 0, 0, SHADOW_STRENGTH); QPixmap shadow = PaintUtils::generateFuzzyRect(size, color, SHADOW_SIZE); it = mShadowCache.insert(key, shadow); } painter->drawPixmap(rect.topLeft() + shadowOffset, it.value()); } void drawText(QPainter* painter, const QRect& rect, const QColor& fgColor, const QString& fullText) const { QFontMetrics fm = mView->fontMetrics(); // Elide text QString text; QHash::const_iterator it = mElidedTextCache.constFind(fullText); if (it == mElidedTextCache.constEnd()) { text = fm.elidedText(fullText, mTextElideMode, rect.width()); mElidedTextCache[fullText] = text; } else { text = it.value(); } // Compute x pos int posX; if (text.length() == fullText.length()) { // Not elided, center text posX = (rect.width() - fm.width(text)) / 2; } else { // Elided, left align posX = 0; } // Draw text painter->setPen(fgColor); painter->drawText(rect.left() + posX, rect.top() + fm.ascent(), text); } void drawRating(QPainter* painter, const QRect& rect, const QVariant& value) { #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE const int rating = value.toInt(); const QRect ratingRect = ratingRectFromIndexRect(rect); const int hoverRating = ratingFromCursorPosition(ratingRect); mRatingPainter.paint(painter, ratingRect, rating, hoverRating); #endif } bool isTextElided(const QString& text) const { QHash::const_iterator it = mElidedTextCache.constFind(text); if (it == mElidedTextCache.constEnd()) { return false; } return it.value().length() < text.length(); } /** * Show a tooltip only if the item has been elided. * This function places the tooltip over the item text. */ void showToolTip(const QModelIndex& index) { if (mDetails == 0 || mDetails == PreviewItemDelegate::RatingDetail) { // No text to display return; } // Gather tip text QStringList textList; bool elided = false; if (mDetails & PreviewItemDelegate::FileNameDetail) { const QString text = index.data().toString(); elided |= isTextElided(text); textList << text; } // FIXME: Duplicated from drawText const KFileItem fileItem = fileItemForIndex(index); const bool isDirOrArchive = ArchiveUtils::fileItemIsDirOrArchive(fileItem); if (mDetails & PreviewItemDelegate::DateDetail) { if (!ArchiveUtils::fileItemIsDirOrArchive(fileItem)) { const QDateTime dt = TimeUtils::dateTimeForFileItem(fileItem); const QString text = QLocale().toString(dt, QLocale::ShortFormat); elided |= isTextElided(text); textList << text; } } if (!isDirOrArchive && (mDetails & PreviewItemDelegate::ImageSizeDetail)) { QSize fullSize; QPixmap thumbnailPix = mView->thumbnailForIndex(index, &fullSize); if (fullSize.isValid()) { - const QString text = QString("%1x%2").arg(fullSize.width()).arg(fullSize.height()); + const QString text = QStringLiteral("%1x%2").arg(fullSize.width()).arg(fullSize.height()); elided |= isTextElided(text); textList << text; } } if (!isDirOrArchive && (mDetails & PreviewItemDelegate::FileSizeDetail)) { const KIO::filesize_t size = fileItem.size(); if (size > 0) { const QString text = KIO::convertSize(size); elided |= isTextElided(text); textList << text; } } if (!elided) { hideToolTip(); return; } bool newTipLabel = !mToolTip; if (!mToolTip) { initToolTip(); } - mToolTip->setText(textList.join("\n")); + mToolTip->setText(textList.join(QLatin1Char('\n'))); QSize tipSize = mToolTip->sizeHint(); // Compute tip position QRect rect = mView->visualRect(index); const int textY = ITEM_MARGIN + mThumbnailSize.height() + ITEM_MARGIN; const int spacing = 1; QRect geometry( QPoint(rect.topLeft() + QPoint((rect.width() - tipSize.width()) / 2, textY + spacing)), tipSize ); if (geometry.left() < 0) { geometry.moveLeft(0); } else if (geometry.right() > mView->viewport()->width()) { geometry.moveRight(mView->viewport()->width()); } // Show tip QParallelAnimationGroup* anim = new QParallelAnimationGroup(); QPropertyAnimation* fadeIn = new QPropertyAnimation(mToolTip, "opacity"); fadeIn->setStartValue(mToolTip->opacity()); fadeIn->setEndValue(1.); anim->addAnimation(fadeIn); if (newTipLabel) { mToolTip->setGeometry(geometry); } else { QPropertyAnimation* move = new QPropertyAnimation(mToolTip, "geometry"); move->setStartValue(mToolTip->geometry()); move->setEndValue(geometry); anim->addAnimation(move); } mToolTipAnimation.reset(anim); mToolTipAnimation->start(); } void hideToolTip() { if (!mToolTip) { return; } QSequentialAnimationGroup* anim = new QSequentialAnimationGroup(); if (mToolTipAnimation->state() == QPropertyAnimation::Stopped) { anim->addPause(500); } QPropertyAnimation* fadeOut = new QPropertyAnimation(mToolTip, "opacity"); fadeOut->setStartValue(mToolTip->opacity()); fadeOut->setEndValue(0.); anim->addAnimation(fadeOut); mToolTipAnimation.reset(anim); mToolTipAnimation->start(); QObject::connect(anim, &QSequentialAnimationGroup::finished, mToolTip.data(), &ToolTipWidget::deleteLater); } int itemWidth() const { return mThumbnailSize.width() + 2 * ITEM_MARGIN; } int ratingRowHeight() const { #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE return qMax(mView->fontMetrics().ascent(), int(KIconLoader::SizeSmall)); #else return 0; #endif } int itemHeight() const { const int lineHeight = mView->fontMetrics().height(); int textHeight = 0; if (mDetails & PreviewItemDelegate::FileNameDetail) { textHeight += lineHeight; } if (mDetails & PreviewItemDelegate::DateDetail) { textHeight += lineHeight; } if (mDetails & PreviewItemDelegate::ImageSizeDetail) { textHeight += lineHeight; } if (mDetails & PreviewItemDelegate::FileSizeDetail) { textHeight += lineHeight; } if (mDetails & PreviewItemDelegate::RatingDetail) { textHeight += ratingRowHeight(); } if (textHeight == 0) { // Keep at least one row of text, so that we can show folder names textHeight = lineHeight; } return mThumbnailSize.height() + textHeight + 3 * ITEM_MARGIN; } void selectIndexUnderCursorIfNoMultiSelection() { if (mView->selectionModel()->selectedIndexes().size() <= 1) { mView->setCurrentIndex(mIndexUnderCursor); } } void updateToggleSelectionButton() { mToggleSelectionButton->setIcon(QIcon::fromTheme( - mView->selectionModel()->isSelected(mIndexUnderCursor) ? "list-remove" : "list-add" + mView->selectionModel()->isSelected(mIndexUnderCursor) ? QStringLiteral("list-remove") : QStringLiteral("list-add") )); } void updateImageButtons() { const KFileItem item = fileItemForIndex(mIndexUnderCursor); const bool isImage = !ArchiveUtils::fileItemIsDirOrArchive(item); mFullScreenButton->setEnabled(isImage); mRotateLeftButton->setEnabled(isImage); mRotateRightButton->setEnabled(isImage); } void updateContextBar() { if (mContextBarActions == PreviewItemDelegate::NoAction) { mContextBar->hide(); return; } const int width = itemWidth(); const int buttonWidth = mRotateRightButton->sizeHint().width(); mFullScreenButton->setVisible(mContextBarActions & PreviewItemDelegate::FullScreenAction); bool rotate = mContextBarActions & PreviewItemDelegate::RotateAction; mRotateLeftButton->setVisible(rotate && width >= 3 * buttonWidth); mRotateRightButton->setVisible(rotate && width >= 4 * buttonWidth); mContextBar->adjustSize(); } void updateViewGridSize() { mView->setGridSize(QSize(itemWidth(), itemHeight())); } }; PreviewItemDelegate::PreviewItemDelegate(ThumbnailView* view) : QItemDelegate(view) , d(new PreviewItemDelegatePrivate) { d->q = this; d->mView = view; view->viewport()->installEventFilter(this); // Set this attribute so that the viewport receives QEvent::HoverMove and // QEvent::HoverLeave events. We use these events in the event filter // installed on the viewport. // Some styles set this attribute themselves (Oxygen and Skulpture do) but // others do not (Plastique, Cleanlooks...) view->viewport()->setAttribute(Qt::WA_Hover); d->mThumbnailSize = view->thumbnailSize(); d->mDetails = FileNameDetail; d->mContextBarActions = SelectionAction | FullScreenAction | RotateAction; d->mTextElideMode = Qt::ElideRight; connect(view, SIGNAL(rowsRemovedSignal(QModelIndex,int,int)), SLOT(slotRowsChanged())); connect(view, SIGNAL(rowsInsertedSignal(QModelIndex,int,int)), SLOT(slotRowsChanged())); connect(view, &ThumbnailView::selectionChangedSignal, [this]() { d->updateToggleSelectionButton(); }); #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE d->mRatingPainter.setAlignment(Qt::AlignHCenter | Qt::AlignBottom); d->mRatingPainter.setLayoutDirection(view->layoutDirection()); d->mRatingPainter.setMaxRating(10); #endif connect(view, SIGNAL(thumbnailSizeChanged(QSize)), SLOT(setThumbnailSize(QSize))); // Button frame d->mContextBar = new QWidget(d->mView->viewport()); d->mContextBar->hide(); d->mToggleSelectionButton = new QToolButton; - d->mToggleSelectionButton->setIcon(QIcon::fromTheme("list-add")); + d->mToggleSelectionButton->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); connect(d->mToggleSelectionButton, &QToolButton::clicked, this, &PreviewItemDelegate::slotToggleSelectionClicked); d->mFullScreenButton = new QToolButton; - d->mFullScreenButton->setIcon(QIcon::fromTheme("view-fullscreen")); + d->mFullScreenButton->setIcon(QIcon::fromTheme(QStringLiteral("view-fullscreen"))); connect(d->mFullScreenButton, &QToolButton::clicked, this, &PreviewItemDelegate::slotFullScreenClicked); d->mRotateLeftButton = new QToolButton; - d->mRotateLeftButton->setIcon(QIcon::fromTheme("object-rotate-left")); + d->mRotateLeftButton->setIcon(QIcon::fromTheme(QStringLiteral("object-rotate-left"))); connect(d->mRotateLeftButton, &QToolButton::clicked, this, &PreviewItemDelegate::slotRotateLeftClicked); d->mRotateRightButton = new QToolButton; - d->mRotateRightButton->setIcon(QIcon::fromTheme("object-rotate-right")); + d->mRotateRightButton->setIcon(QIcon::fromTheme(QStringLiteral("object-rotate-right"))); connect(d->mRotateRightButton, &QToolButton::clicked, this, &PreviewItemDelegate::slotRotateRightClicked); QHBoxLayout* layout = new QHBoxLayout(d->mContextBar); layout->setMargin(2); layout->setSpacing(2); layout->addWidget(d->mToggleSelectionButton); layout->addWidget(d->mFullScreenButton); layout->addWidget(d->mRotateLeftButton); layout->addWidget(d->mRotateRightButton); // Save button d->mSaveButton = new QToolButton(d->mView->viewport()); - d->mSaveButton->setIcon(QIcon::fromTheme("document-save")); + d->mSaveButton->setIcon(QIcon::fromTheme(QStringLiteral("document-save"))); d->mSaveButton->hide(); connect(d->mSaveButton, &QToolButton::clicked, this, &PreviewItemDelegate::slotSaveClicked); } PreviewItemDelegate::~PreviewItemDelegate() { delete d; } QSize PreviewItemDelegate::sizeHint(const QStyleOptionViewItem & /*option*/, const QModelIndex & /*index*/) const { return d->mView->gridSize(); } bool PreviewItemDelegate::eventFilter(QObject* object, QEvent* event) { if (object == d->mView->viewport()) { switch (event->type()) { case QEvent::ToolTip: return true; case QEvent::HoverMove: case QEvent::HoverLeave: return d->hoverEventFilter(static_cast(event)); case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: return d->mouseButtonEventFilter(event->type()); default: return false; } } else { // Necessary for the item editor to work correctly (especially closing // the editor with the Escape key) return QItemDelegate::eventFilter(object, event); } } void PreviewItemDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const { int thumbnailHeight = d->mThumbnailSize.height(); QSize fullSize; QPixmap thumbnailPix = d->mView->thumbnailForIndex(index, &fullSize); const KFileItem fileItem = fileItemForIndex(index); const bool opaque = !thumbnailPix.hasAlphaChannel(); const bool isDirOrArchive = ArchiveUtils::fileItemIsDirOrArchive(fileItem); QRect rect = option.rect; const bool selected = option.state & QStyle::State_Selected; const bool underMouse = option.state & QStyle::State_MouseOver; const bool hasFocus = option.state & QStyle::State_HasFocus; const QWidget* viewport = d->mView->viewport(); #ifdef DEBUG_DRAW_BORDER painter->setPen(Qt::red); painter->setBrush(Qt::NoBrush); painter->drawRect(rect); #endif // Select color group QPalette::ColorGroup cg; if ((option.state & QStyle::State_Enabled) && (option.state & QStyle::State_Active)) { cg = QPalette::Normal; } else if ((option.state & QStyle::State_Enabled)) { cg = QPalette::Inactive; } else { cg = QPalette::Disabled; } // Select colors QColor bgColor, borderColor, fgColor; fgColor = viewport->palette().color(viewport->foregroundRole()); if (selected || underMouse) { bgColor = option.palette.color(cg, QPalette::Highlight); if (hasFocus) { borderColor = bgColor.dark(FOCUS_BORDER_DARKNESS); } else { borderColor = bgColor.dark(SELECTION_BORDER_DARKNESS); } } else { bgColor = viewport->palette().color(viewport->backgroundRole()); if (hasFocus) { borderColor = fgColor; } else { borderColor = bgColor.light(200); } } // Compute thumbnailRect QRect thumbnailRect = QRect( rect.left() + (rect.width() - thumbnailPix.width()) / 2, rect.top() + (thumbnailHeight - thumbnailPix.height()) + ITEM_MARGIN, thumbnailPix.width(), thumbnailPix.height()); // Draw background const QRect backgroundRect = thumbnailRect.adjusted(-ITEM_MARGIN, -ITEM_MARGIN, ITEM_MARGIN, ITEM_MARGIN); if (selected) { d->drawBackground(painter, backgroundRect, bgColor, borderColor); } else if (underMouse) { painter->setOpacity(0.2); d->drawBackground(painter, backgroundRect, bgColor, borderColor); painter->setOpacity(1.); } else if (opaque) { d->drawShadow(painter, thumbnailRect); } // Draw thumbnail if (opaque) { painter->setPen(borderColor); painter->setRenderHint(QPainter::Antialiasing, false); QRect borderRect = thumbnailRect.adjusted(-1, -1, 0, 0); painter->drawRect(borderRect); } else if (hasFocus && !selected) { painter->setPen(option.palette.color(cg, QPalette::Highlight)); painter->setRenderHint(QPainter::Antialiasing, false); QLine underLine = QLine(thumbnailRect.bottomLeft(), thumbnailRect.bottomRight()); underLine.translate(0, 3); painter->drawLine(underLine); } painter->drawPixmap(thumbnailRect.left(), thumbnailRect.top(), thumbnailPix); // Draw modified indicator bool isModified = d->mView->isModified(index); if (isModified) { // Draws a pixmap of the save button frame, as an indicator that // the image has been modified QPoint framePosition = d->saveButtonPosition(rect); d->initSaveButtonPixmap(); painter->drawPixmap(framePosition, d->mSaveButtonPixmap); } // Draw busy indicator if (d->mView->isBusy(index)) { QPixmap pix = d->mView->busySequenceCurrentPixmap(); painter->drawPixmap( thumbnailRect.left() + (thumbnailRect.width() - pix.width()) / 2, thumbnailRect.top() + (thumbnailRect.height() - pix.height()) / 2, pix); } if (index == d->mIndexUnderCursor) { // Show bar again: if the thumbnail has changed, we may need to update // its position. Don't do it if we are over rotate buttons, though: it // would not be nice to move the button now, the user may want to // rotate the image one more time. // The button will get moved when the mouse leaves. if (!d->mRotateLeftButton->underMouse() && !d->mRotateRightButton->underMouse()) { d->showContextBar(rect, thumbnailPix); } if (isModified) { // If we just rotated the image with the buttons from the // button frame, we need to show the save button frame right now. d->showSaveButton(rect); } else { d->mSaveButton->hide(); } } QRect textRect( rect.left() + ITEM_MARGIN, rect.top() + 2 * ITEM_MARGIN + thumbnailHeight, rect.width() - 2 * ITEM_MARGIN, d->mView->fontMetrics().height()); if (isDirOrArchive || (d->mDetails & PreviewItemDelegate::FileNameDetail)) { d->drawText(painter, textRect, fgColor, index.data().toString()); textRect.moveTop(textRect.bottom()); } if (!isDirOrArchive && (d->mDetails & PreviewItemDelegate::DateDetail)) { const QDateTime dt = TimeUtils::dateTimeForFileItem(fileItem); d->drawText(painter, textRect, fgColor, QLocale().toString(dt, QLocale::ShortFormat)); textRect.moveTop(textRect.bottom()); } if (!isDirOrArchive && (d->mDetails & PreviewItemDelegate::ImageSizeDetail)) { if (fullSize.isValid()) { - const QString text = QString("%1x%2").arg(fullSize.width()).arg(fullSize.height()); + const QString text = QStringLiteral("%1x%2").arg(fullSize.width()).arg(fullSize.height()); d->drawText(painter, textRect, fgColor, text); textRect.moveTop(textRect.bottom()); } } if (!isDirOrArchive && (d->mDetails & PreviewItemDelegate::FileSizeDetail)) { const KIO::filesize_t size = fileItem.size(); if (size > 0) { const QString st = KIO::convertSize(size); d->drawText(painter, textRect, fgColor, st); textRect.moveTop(textRect.bottom()); } } if (!isDirOrArchive && (d->mDetails & PreviewItemDelegate::RatingDetail)) { #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE d->drawRating(painter, rect, index.data(SemanticInfoDirModel::RatingRole)); #endif } #ifdef DEBUG_DRAW_CURRENT if (d->mView->currentIndex() == index) { painter->fillRect(rect.left(), rect.top(), 12, 12, Qt::red); } #endif } void PreviewItemDelegate::setThumbnailSize(const QSize& value) { d->mThumbnailSize = value; d->updateViewGridSize(); d->updateContextBar(); d->mElidedTextCache.clear(); } void PreviewItemDelegate::slotSaveClicked() { saveDocumentRequested(urlForIndex(d->mIndexUnderCursor)); } void PreviewItemDelegate::slotRotateLeftClicked() { d->selectIndexUnderCursorIfNoMultiSelection(); rotateDocumentLeftRequested(urlForIndex(d->mIndexUnderCursor)); } void PreviewItemDelegate::slotRotateRightClicked() { d->selectIndexUnderCursorIfNoMultiSelection(); rotateDocumentRightRequested(urlForIndex(d->mIndexUnderCursor)); } void PreviewItemDelegate::slotFullScreenClicked() { showDocumentInFullScreenRequested(urlForIndex(d->mIndexUnderCursor)); } void PreviewItemDelegate::slotToggleSelectionClicked() { d->mView->selectionModel()->select(d->mIndexUnderCursor, QItemSelectionModel::Toggle); } PreviewItemDelegate::ThumbnailDetails PreviewItemDelegate::thumbnailDetails() const { return d->mDetails; } void PreviewItemDelegate::setThumbnailDetails(PreviewItemDelegate::ThumbnailDetails details) { d->mDetails = details; d->updateViewGridSize(); d->mView->scheduleDelayedItemsLayout(); } PreviewItemDelegate::ContextBarActions PreviewItemDelegate::contextBarActions() const { return d->mContextBarActions; } void PreviewItemDelegate::setContextBarActions(PreviewItemDelegate::ContextBarActions actions) { d->mContextBarActions = actions; d->updateContextBar(); } Qt::TextElideMode PreviewItemDelegate::textElideMode() const { return d->mTextElideMode; } void PreviewItemDelegate::setTextElideMode(Qt::TextElideMode mode) { if (d->mTextElideMode == mode) { return; } d->mTextElideMode = mode; d->mElidedTextCache.clear(); d->mView->viewport()->update(); } void PreviewItemDelegate::slotRowsChanged() { // We need to update hover ui because the current index may have // disappeared: for example if the current image is removed with "del". QPoint pos = d->mView->viewport()->mapFromGlobal(QCursor::pos()); QModelIndex index = d->mView->indexAt(pos); d->updateHoverUi(index); } QWidget * PreviewItemDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/) const { return new ItemEditor(parent); } void PreviewItemDelegate::setEditorData(QWidget* widget, const QModelIndex& index) const { ItemEditor* edit = qobject_cast(widget); if (!edit) { return; } edit->setText(index.data().toString()); } void PreviewItemDelegate::updateEditorGeometry(QWidget* widget, const QStyleOptionViewItem& option, const QModelIndex& index) const { ItemEditor* edit = qobject_cast(widget); if (!edit) { return; } QString text = index.data().toString(); - int textWidth = edit->fontMetrics().width(" " + text + " "); + int textWidth = edit->fontMetrics().width(QStringLiteral(" ") + text + QStringLiteral(" ")); QRect textRect( option.rect.left() + (option.rect.width() - textWidth) / 2, option.rect.top() + 2 * ITEM_MARGIN + d->mThumbnailSize.height(), textWidth, edit->sizeHint().height()); edit->setGeometry(textRect); } void PreviewItemDelegate::setModelData(QWidget* widget, QAbstractItemModel* model, const QModelIndex& index) const { ItemEditor* edit = qobject_cast(widget); if (!edit) { return; } if (index.data().toString() != edit->text()) { model->setData(index, edit->text(), Qt::EditRole); } } } // namespace diff --git a/lib/thumbnailview/previewitemdelegate.h b/lib/thumbnailview/previewitemdelegate.h index 2e5c85f0..53b6dcc8 100644 --- a/lib/thumbnailview/previewitemdelegate.h +++ b/lib/thumbnailview/previewitemdelegate.h @@ -1,126 +1,126 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 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, Cambridge, MA 02110-1301, USA. */ #ifndef PREVIEWITEMDELEGATE_H #define PREVIEWITEMDELEGATE_H #include // Qt #include // KDE // Local class QUrl; namespace Gwenview { class ThumbnailView; struct PreviewItemDelegatePrivate; /** * An ItemDelegate which generates thumbnails for images. It also makes sure * all items are of the same size. */ class GWENVIEWLIB_EXPORT PreviewItemDelegate : public QItemDelegate { Q_OBJECT public: PreviewItemDelegate(ThumbnailView*); ~PreviewItemDelegate(); enum ContextBarAction { NoAction = 0, SelectionAction = 1, FullScreenAction = 2, RotateAction = 4 }; Q_DECLARE_FLAGS(ContextBarActions, ContextBarAction) enum ThumbnailDetail { FileNameDetail = 1, DateDetail = 2, RatingDetail = 4, ImageSizeDetail = 8, FileSizeDetail = 16 }; // FIXME: Find out why this cause problems with Qt::Alignment in // PreviewItemDelegate! Q_DECLARE_FLAGS(ThumbnailDetails, ThumbnailDetail) /** * Returns which thumbnail details are shown */ ThumbnailDetails thumbnailDetails() const; void setThumbnailDetails(ThumbnailDetails); ContextBarActions contextBarActions() const; void setContextBarActions(ContextBarActions); Qt::TextElideMode textElideMode() const; void setTextElideMode(Qt::TextElideMode); - QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const Q_DECL_OVERRIDE; - void setEditorData(QWidget* editor, const QModelIndex& index) const Q_DECL_OVERRIDE; - void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const Q_DECL_OVERRIDE; - void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const Q_DECL_OVERRIDE; + QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override; + void setEditorData(QWidget* editor, const QModelIndex& index) const override; + void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override; + void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const override; - void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const Q_DECL_OVERRIDE; - QSize sizeHint(const QStyleOptionViewItem & /*option*/, const QModelIndex & /*index*/) const Q_DECL_OVERRIDE; + void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const override; + QSize sizeHint(const QStyleOptionViewItem & /*option*/, const QModelIndex & /*index*/) const override; Q_SIGNALS: void saveDocumentRequested(const QUrl&); void rotateDocumentLeftRequested(const QUrl&); void rotateDocumentRightRequested(const QUrl&); void showDocumentInFullScreenRequested(const QUrl&); void setDocumentRatingRequested(const QUrl&, int rating); private Q_SLOTS: void setThumbnailSize(const QSize&); void slotSaveClicked(); void slotRotateLeftClicked(); void slotRotateRightClicked(); void slotFullScreenClicked(); void slotToggleSelectionClicked(); void slotRowsChanged(); protected: - bool eventFilter(QObject*, QEvent*) Q_DECL_OVERRIDE; + bool eventFilter(QObject*, QEvent*) override; private: PreviewItemDelegatePrivate* const d; friend struct PreviewItemDelegatePrivate; }; } // namespace // See upper Q_DECLARE_OPERATORS_FOR_FLAGS(Gwenview::PreviewItemDelegate::ThumbnailDetails) Q_DECLARE_OPERATORS_FOR_FLAGS(Gwenview::PreviewItemDelegate::ContextBarActions) #endif /* PREVIEWITEMDELEGATE_H */ diff --git a/lib/thumbnailview/thumbnailbarview.cpp b/lib/thumbnailview/thumbnailbarview.cpp index aee74347..89c05c1d 100644 --- a/lib/thumbnailview/thumbnailbarview.cpp +++ b/lib/thumbnailview/thumbnailbarview.cpp @@ -1,547 +1,547 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 Aurélien Gâteau Copyright 2008 Ilya Konkov 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 "thumbnailbarview.h" // Qt #include #include #include #include #include #include #include #include #include #ifdef WINDOWS_PROXY_STYLE #include #endif // KDE #include // Local #include "lib/hud/hudtheme.h" #include "lib/paintutils.h" #include "lib/thumbnailview/abstractthumbnailviewhelper.h" #include "gwenviewconfig.h" namespace Gwenview { /** * Duration in ms of the smooth scroll */ const int SMOOTH_SCROLL_DURATION = 250; /** * Space between the item outer rect and the content, and between the * thumbnail and the caption */ const int ITEM_MARGIN = 5; /** How dark is the shadow, 0 is invisible, 255 is as dark as possible */ const int SHADOW_STRENGTH = 127; /** How many pixels around the thumbnail are shadowed */ const int SHADOW_SIZE = 4; struct ThumbnailBarItemDelegatePrivate { // Key is height * 1000 + width typedef QMap ShadowCache; mutable ShadowCache mShadowCache; ThumbnailBarItemDelegate* q; ThumbnailView* mView; QToolButton* mToggleSelectionButton; QColor mBorderColor; QPersistentModelIndex mIndexUnderCursor; void setupToggleSelectionButton() { mToggleSelectionButton = new QToolButton(mView->viewport()); - mToggleSelectionButton->setIcon(QIcon::fromTheme("list-add")); + mToggleSelectionButton->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); mToggleSelectionButton->hide(); QObject::connect(mToggleSelectionButton, &QToolButton::clicked, q, &ThumbnailBarItemDelegate::toggleSelection); } void showToolTip(QHelpEvent* helpEvent) { QModelIndex index = mView->indexAt(helpEvent->pos()); if (!index.isValid()) { return; } QString fullText = index.data().toString(); QPoint pos = QCursor::pos(); QToolTip::showText(pos, fullText, mView); } void drawShadow(QPainter* painter, const QRect& rect) const { const QPoint shadowOffset(-SHADOW_SIZE, -SHADOW_SIZE + 1); int key = rect.height() * 1000 + rect.width(); ShadowCache::Iterator it = mShadowCache.find(key); if (it == mShadowCache.end()) { QSize size = QSize(rect.width() + 2 * SHADOW_SIZE, rect.height() + 2 * SHADOW_SIZE); QColor color(0, 0, 0, SHADOW_STRENGTH); QPixmap shadow = PaintUtils::generateFuzzyRect(size, color, SHADOW_SIZE); it = mShadowCache.insert(key, shadow); } painter->drawPixmap(rect.topLeft() + shadowOffset, it.value()); } bool hoverEventFilter(QHoverEvent* event) { QModelIndex index = mView->indexAt(event->pos()); if (index != mIndexUnderCursor) { updateHoverUi(index); } return false; } void updateHoverUi(const QModelIndex& index) { mIndexUnderCursor = index; if (mIndexUnderCursor.isValid() && GwenviewConfig::thumbnailActions() != ThumbnailActions::None) { updateToggleSelectionButton(); const QRect rect = mView->visualRect(mIndexUnderCursor); mToggleSelectionButton->move(rect.topLeft() + QPoint(2, 2)); mToggleSelectionButton->show(); } else { mToggleSelectionButton->hide(); } } void updateToggleSelectionButton() { bool isSelected = mView->selectionModel()->isSelected(mIndexUnderCursor); - mToggleSelectionButton->setIcon(QIcon::fromTheme(isSelected ? "list-remove" : "list-add")); + mToggleSelectionButton->setIcon(QIcon::fromTheme(isSelected ? QStringLiteral("list-remove") : QStringLiteral("list-add"))); } }; ThumbnailBarItemDelegate::ThumbnailBarItemDelegate(ThumbnailView* view) : QAbstractItemDelegate(view) , d(new ThumbnailBarItemDelegatePrivate) { d->q = this; d->mView = view; d->setupToggleSelectionButton(); view->viewport()->installEventFilter(this); // Set this attribute so that the viewport receives QEvent::HoverMove and // QEvent::HoverLeave events. We use these events in the event filter // installed on the viewport. // Some styles set this attribute themselves (Oxygen and Skulpture do) but // others do not (Plastique, Cleanlooks...) view->viewport()->setAttribute(Qt::WA_Hover); d->mBorderColor = PaintUtils::alphaAdjustedF(QColor(Qt::white), 0.65); connect(view, &ThumbnailView::selectionChangedSignal, [this]() { d->updateToggleSelectionButton(); }); } QSize ThumbnailBarItemDelegate::sizeHint(const QStyleOptionViewItem & /*option*/, const QModelIndex & index) const { QSize size; if (d->mView->thumbnailScaleMode() == ThumbnailView::ScaleToFit) { size = d->mView->gridSize(); } else { QPixmap thumbnailPix = d->mView->thumbnailForIndex(index); size = thumbnailPix.size(); size.rwidth() += ITEM_MARGIN * 2; size.rheight() += ITEM_MARGIN * 2; } return size; } bool ThumbnailBarItemDelegate::eventFilter(QObject*, QEvent* event) { switch (event->type()) { case QEvent::ToolTip: d->showToolTip(static_cast(event)); return true; case QEvent::HoverMove: case QEvent::HoverLeave: return d->hoverEventFilter(static_cast(event)); default: break; } return false; } void ThumbnailBarItemDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const { bool isSelected = option.state & QStyle::State_Selected; bool isCurrent = d->mView->selectionModel()->currentIndex() == index; QPixmap thumbnailPix = d->mView->thumbnailForIndex(index); QRect rect = option.rect; QStyleOptionViewItem opt = option; const QWidget* widget = opt.widget; QStyle* style = widget ? widget->style() : QApplication::style(); if (isSelected && !isCurrent) { // Draw selected but not current item backgrounds with some transparency // so that the current item stands out. painter->setOpacity(.33); } style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, widget); painter->setOpacity(1); // Draw thumbnail if (!thumbnailPix.isNull()) { QRect thumbnailRect = QRect( rect.left() + (rect.width() - thumbnailPix.width()) / 2, rect.top() + (rect.height() - thumbnailPix.height()) / 2 - 1, thumbnailPix.width(), thumbnailPix.height()); if (!thumbnailPix.hasAlphaChannel()) { d->drawShadow(painter, thumbnailRect); painter->setPen(d->mBorderColor); painter->setRenderHint(QPainter::Antialiasing, false); QRect borderRect = thumbnailRect.adjusted(-1, -1, 0, 0); painter->drawRect(borderRect); } painter->drawPixmap(thumbnailRect.left(), thumbnailRect.top(), thumbnailPix); // Draw busy indicator if (d->mView->isBusy(index)) { QPixmap pix = d->mView->busySequenceCurrentPixmap(); painter->drawPixmap( thumbnailRect.left() + (thumbnailRect.width() - pix.width()) / 2, thumbnailRect.top() + (thumbnailRect.height() - pix.height()) / 2, pix); } } } void ThumbnailBarItemDelegate::toggleSelection() { d->mView->selectionModel()->select(d->mIndexUnderCursor, QItemSelectionModel::Toggle); } ThumbnailBarItemDelegate::~ThumbnailBarItemDelegate() { delete d; } //this is disabled by David Edmundson as I can't figure out how to port it //I hope with breeze being the default we don't want to start making our own styles anyway #ifdef WINDOWS_PROXY_STYLE /** * This proxy style makes it possible to override the value returned by * styleHint() which leads to not-so-nice results with some styles. * * We cannot use QProxyStyle because it takes ownership of the base style, * which causes crash when user change styles. */ class ProxyStyle : public QWindowsStyle { public: ProxyStyle() : QWindowsStyle() { } void drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p, const QWidget *w = 0) const { QApplication::style()->drawPrimitive(pe, opt, p, w); } void drawControl(ControlElement element, const QStyleOption *opt, QPainter *p, const QWidget *w = 0) const { QApplication::style()->drawControl(element, opt, p, w); } void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p, const QWidget *w = 0) const { QApplication::style()->drawComplexControl(cc, opt, p, w); } int styleHint(StyleHint sh, const QStyleOption *opt = 0, const QWidget *w = 0, QStyleHintReturn *shret = 0) const { switch (sh) { case SH_ItemView_ShowDecorationSelected: // We want the highlight to cover our thumbnail return true; case SH_ScrollView_FrameOnlyAroundContents: // Ensure the frame does not include the scrollbar. This ensure the // scrollbar touches the edge of the window and thus can touch the // edge of the screen when maximized return false; default: return QApplication::style()->styleHint(sh, opt, w, shret); } } void polish(QApplication* application) { QApplication::style()->polish(application); } void polish(QPalette& palette) { QApplication::style()->polish(palette); } void polish(QWidget* widget) { QApplication::style()->polish(widget); } void unpolish(QWidget* widget) { QApplication::style()->unpolish(widget); } void unpolish(QApplication* application) { QApplication::style()->unpolish(application); } int pixelMetric(PixelMetric pm, const QStyleOption* opt, const QWidget* widget) const { switch (pm) { case PM_MaximumDragDistance: // Ensure the fullscreen thumbnailbar does not go away while // dragging the scrollbar if the mouse cursor is too far away from // the widget return -1; default: return QApplication::style()->pixelMetric(pm, opt, widget); } } }; #endif// WINDOWS_PROXY_STYLE typedef int (QSize::*QSizeDimension)() const; struct ThumbnailBarViewPrivate { ThumbnailBarView* q; QStyle* mStyle; QTimeLine* mTimeLine; Qt::Orientation mOrientation; int mRowCount; QScrollBar* scrollBar() const { return mOrientation == Qt::Horizontal ? q->horizontalScrollBar() : q->verticalScrollBar(); } QSizeDimension mainDimension() const { return mOrientation == Qt::Horizontal ? &QSize::width : &QSize::height; } QSizeDimension oppositeDimension() const { return mOrientation == Qt::Horizontal ? &QSize::height : &QSize::width; } void smoothScrollTo(const QModelIndex& index) { if (!index.isValid()) { return; } const QRect rect = q->visualRect(index); int oldValue = scrollBar()->value(); int newValue = scrollToValue(rect); if (mTimeLine->state() == QTimeLine::Running) { mTimeLine->stop(); } mTimeLine->setFrameRange(oldValue, newValue); mTimeLine->start(); } int scrollToValue(const QRect& rect) { // This code is a much simplified version of // QListViewPrivate::horizontalScrollToValue() const QRect area = q->viewport()->rect(); int value = scrollBar()->value(); if (mOrientation == Qt::Horizontal) { if (q->isRightToLeft()) { value += (area.width() - rect.width()) / 2 - rect.left(); } else { value += rect.left() - (area.width() - rect.width()) / 2; } } else { value += rect.top() - (area.height() - rect.height()) / 2; } return value; } void updateMinMaxSizes() { QSizeDimension dimension = oppositeDimension(); int scrollBarSize = (scrollBar()->sizeHint().*dimension)(); QSize minSize(0, mRowCount * 48 + scrollBarSize); QSize maxSize(QWIDGETSIZE_MAX, mRowCount * 256 + scrollBarSize); if (mOrientation == Qt::Vertical) { minSize.transpose(); maxSize.transpose(); } q->setMinimumSize(minSize); q->setMaximumSize(maxSize); } void updateThumbnailSize() { QSizeDimension dimension = oppositeDimension(); int scrollBarSize = (scrollBar()->sizeHint().*dimension)(); int widgetSize = (q->size().*dimension)(); if (mRowCount > 1) { // Decrease widgetSize because otherwise the view sometimes wraps at // mRowCount-1 instead of mRowCount. Probably because gridSize * // mRowCount is too close to widgetSize. --widgetSize; } int gridWidth, gridHeight; if (mOrientation == Qt::Horizontal) { gridHeight = (widgetSize - scrollBarSize - 2 * q->frameWidth()) / mRowCount; gridWidth = qRound(gridHeight * q->thumbnailAspectRatio()); } else { gridWidth = (widgetSize - scrollBarSize - 2 * q->frameWidth()) / mRowCount; gridHeight = qRound(gridWidth / q->thumbnailAspectRatio()); } if (q->thumbnailScaleMode() == ThumbnailView::ScaleToFit) { q->setGridSize(QSize(gridWidth, gridHeight)); } q->setThumbnailWidth(gridWidth - ITEM_MARGIN * 2); } }; ThumbnailBarView::ThumbnailBarView(QWidget* parent) : ThumbnailView(parent) , d(new ThumbnailBarViewPrivate) { d->q = this; d->mTimeLine = new QTimeLine(SMOOTH_SCROLL_DURATION, this); connect(d->mTimeLine, &QTimeLine::frameChanged, this, &ThumbnailBarView::slotFrameChanged); d->mRowCount = 1; d->mOrientation = Qt::Vertical; // To pass value-has-changed check in setOrientation() setOrientation(Qt::Horizontal); setObjectName(QLatin1String("thumbnailBarView")); setWrapping(true); #ifdef WINDOWS_PROXY_STYLE d->mStyle = new ProxyStyle; setStyle(d->mStyle); #endif } ThumbnailBarView::~ThumbnailBarView() { #ifdef WINDOWS_PROXY_STYLE delete d->mStyle; #endif delete d; } Qt::Orientation ThumbnailBarView::orientation() const { return d->mOrientation; } void ThumbnailBarView::setOrientation(Qt::Orientation orientation) { if (d->mOrientation == orientation) { return; } d->mOrientation = orientation; if (d->mOrientation == Qt::Vertical) { setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); setFlow(LeftToRight); } else { setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setFlow(TopToBottom); } d->updateMinMaxSizes(); } void ThumbnailBarView::slotFrameChanged(int value) { d->scrollBar()->setValue(value); } void ThumbnailBarView::resizeEvent(QResizeEvent *event) { ThumbnailView::resizeEvent(event); d->updateThumbnailSize(); } void ThumbnailBarView::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) { ThumbnailView::selectionChanged(selected, deselected); QModelIndexList oldList = deselected.indexes(); QModelIndexList newList = selected.indexes(); // Only scroll the list if the user went from one image to another. If the // user just unselected one image from a set of two, he might want to // reselect it again, scrolling the thumbnails would prevent him from // reselecting it by clicking again without moving the mouse. if (oldList.count() == 1 && newList.count() == 1 && isVisible()) { d->smoothScrollTo(newList.first()); } } void ThumbnailBarView::wheelEvent(QWheelEvent* event) { d->scrollBar()->setValue(d->scrollBar()->value() - event->delta()); } int ThumbnailBarView::rowCount() const { return d->mRowCount; } void ThumbnailBarView::setRowCount(int rowCount) { Q_ASSERT(rowCount > 0); d->mRowCount = rowCount; d->updateMinMaxSizes(); d->updateThumbnailSize(); } } // namespace diff --git a/lib/thumbnailview/thumbnailbarview.h b/lib/thumbnailview/thumbnailbarview.h index 14080da3..9f604d0e 100644 --- a/lib/thumbnailview/thumbnailbarview.h +++ b/lib/thumbnailview/thumbnailbarview.h @@ -1,89 +1,89 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 Aurélien Gâteau Copyright 2008 Ilya Konkov 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. */ #ifndef THUMBNAILBARVIEW_H #define THUMBNAILBARVIEW_H #include // Qt #include // KDE // Local #include namespace Gwenview { struct ThumbnailBarItemDelegatePrivate; class GWENVIEWLIB_EXPORT ThumbnailBarItemDelegate : public QAbstractItemDelegate { Q_OBJECT public: ThumbnailBarItemDelegate(ThumbnailView*); - ~ThumbnailBarItemDelegate() Q_DECL_OVERRIDE; + ~ThumbnailBarItemDelegate() override; - void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const Q_DECL_OVERRIDE; - QSize sizeHint(const QStyleOptionViewItem & /*option*/, const QModelIndex & /*index*/) const Q_DECL_OVERRIDE; + void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const override; + QSize sizeHint(const QStyleOptionViewItem & /*option*/, const QModelIndex & /*index*/) const override; protected: - bool eventFilter(QObject*, QEvent*) Q_DECL_OVERRIDE; + bool eventFilter(QObject*, QEvent*) override; private Q_SLOTS: void toggleSelection(); private: ThumbnailBarItemDelegatePrivate* const d; friend struct ThumbnailBarItemDelegatePrivate; }; struct ThumbnailBarViewPrivate; class GWENVIEWLIB_EXPORT ThumbnailBarView : public ThumbnailView { Q_OBJECT public: ThumbnailBarView(QWidget* = 0); ~ThumbnailBarView(); Qt::Orientation orientation() const; void setOrientation(Qt::Orientation); int rowCount() const; void setRowCount(int); protected: - void resizeEvent(QResizeEvent * event) Q_DECL_OVERRIDE; - void wheelEvent(QWheelEvent* event) Q_DECL_OVERRIDE; - void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) Q_DECL_OVERRIDE; + void resizeEvent(QResizeEvent * event) override; + void wheelEvent(QWheelEvent* event) override; + void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) override; private Q_SLOTS: void slotFrameChanged(int); private: ThumbnailBarViewPrivate* const d; }; } // namespace #endif /* THUMBNAILBARVIEW_H */ diff --git a/lib/thumbnailview/thumbnailslider.cpp b/lib/thumbnailview/thumbnailslider.cpp index 51ac46af..2c7fa2c9 100644 --- a/lib/thumbnailview/thumbnailslider.cpp +++ b/lib/thumbnailview/thumbnailslider.cpp @@ -1,73 +1,73 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2009 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, Cambridge, MA 02110-1301, USA. */ // Self #include "thumbnailslider.h" // Local #include // Qt #include // KDE namespace Gwenview { struct ThumbnailSliderPrivate { }; ThumbnailSlider::ThumbnailSlider(QWidget* parent) : ZoomSlider(parent) , d(new ThumbnailSliderPrivate) { connect(slider(), SIGNAL(actionTriggered(int)), SLOT(slotActionTriggered(int))); slider()->setRange(ThumbnailView::MinThumbnailSize, ThumbnailView::MaxThumbnailSize); } ThumbnailSlider::~ThumbnailSlider() { delete d; } void ThumbnailSlider::slotActionTriggered(int actionTriggered) { updateToolTip(); if (actionTriggered != QAbstractSlider::SliderNoAction) { // If we are updating because of a direct action on the slider, show // the tooltip immediately. const QPoint pos = slider()->mapToGlobal(QPoint(0, slider()->height() / 2)); QToolTip::showText(pos, slider()->toolTip(), slider()); } } void ThumbnailSlider::updateToolTip() { // FIXME: i18n? const int size = slider()->sliderPosition(); - const QString text = QString("%1 x %2").arg(size).arg(size); + const QString text = QStringLiteral("%1 x %2").arg(size).arg(size); slider()->setToolTip(text); } } // namespace diff --git a/lib/thumbnailview/thumbnailview.cpp b/lib/thumbnailview/thumbnailview.cpp index 0f39ed05..52202bc5 100644 --- a/lib/thumbnailview/thumbnailview.cpp +++ b/lib/thumbnailview/thumbnailview.cpp @@ -1,976 +1,973 @@ /* 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. */ #include "thumbnailview.h" // Std #include // Qt #include #include #include #include #include #include #include #include #include #include #include #include #include // KDE #include #include #include #include // Local #include "abstractdocumentinfoprovider.h" #include "abstractthumbnailviewhelper.h" #include "archiveutils.h" #include "dragpixmapgenerator.h" #include "mimetypeutils.h" #include "urlutils.h" #include #include namespace Gwenview { #undef ENABLE_LOG #undef LOG //#define ENABLE_LOG #ifdef ENABLE_LOG #define LOG(x) //qDebug() << x #else #define LOG(x) ; #endif /** How many msec to wait before starting to smooth thumbnails */ const int SMOOTH_DELAY = 500; const int WHEEL_ZOOM_MULTIPLIER = 4; static KFileItem fileItemForIndex(const QModelIndex& index) { if (!index.isValid()) { LOG("Invalid index"); return KFileItem(); } QVariant data = index.data(KDirModel::FileItemRole); return qvariant_cast(data); } static QUrl urlForIndex(const QModelIndex& index) { KFileItem item = fileItemForIndex(index); return item.isNull() ? QUrl() : item.url(); } struct Thumbnail { Thumbnail(const QPersistentModelIndex& index_, const QDateTime& mtime) : mIndex(index_) , mModificationTime(mtime) , mFileSize(0) , mRough(true) , mWaitingForThumbnail(true) {} Thumbnail() : mFileSize(0) , mRough(true) , mWaitingForThumbnail(true) {} /** * Init the thumbnail based on a icon */ void initAsIcon(const QPixmap& pix) { mGroupPix = pix; int largeGroupSize = ThumbnailGroup::pixelSize(ThumbnailGroup::Large); mFullSize = QSize(largeGroupSize, largeGroupSize); } bool isGroupPixAdaptedForSize(int size) const { if (mWaitingForThumbnail) { return false; } if (mGroupPix.isNull()) { return false; } const int groupSize = qMax(mGroupPix.width(), mGroupPix.height()); if (groupSize >= size) { return true; } // groupSize is less than size, but this may be because the full image // is the same size as groupSize return groupSize == qMax(mFullSize.width(), mFullSize.height()); } void prepareForRefresh(const QDateTime& mtime) { mModificationTime = mtime; mFileSize = 0; mGroupPix = QPixmap(); mAdjustedPix = QPixmap(); mFullSize = QSize(); mRealFullSize = QSize(); mRough = true; mWaitingForThumbnail = true; } QPersistentModelIndex mIndex; QDateTime mModificationTime; /// The pix loaded from .thumbnails/{large,normal} QPixmap mGroupPix; /// Scaled version of mGroupPix, adjusted to ThumbnailView::thumbnailSize QPixmap mAdjustedPix; /// Size of the full image QSize mFullSize; /// Real size of the full image, invalid unless the thumbnail /// represents a raster image (not an icon) QSize mRealFullSize; /// File size of the full image KIO::filesize_t mFileSize; /// Whether mAdjustedPix represents has been scaled using fast or smooth /// transformation bool mRough; /// Set to true if mGroupPix should be replaced with a real thumbnail bool mWaitingForThumbnail; }; typedef QHash ThumbnailForUrl; typedef QQueue UrlQueue; typedef QSet PersistentModelIndexSet; struct ThumbnailViewPrivate { ThumbnailView* q; ThumbnailView::ThumbnailScaleMode mScaleMode; QSize mThumbnailSize; qreal mThumbnailAspectRatio; AbstractDocumentInfoProvider* mDocumentInfoProvider; AbstractThumbnailViewHelper* mThumbnailViewHelper; ThumbnailForUrl mThumbnailForUrl; QTimer mScheduledThumbnailGenerationTimer; UrlQueue mSmoothThumbnailQueue; QTimer mSmoothThumbnailTimer; QPixmap mWaitingThumbnail; QPointer mThumbnailProvider; PersistentModelIndexSet mBusyIndexSet; KPixmapSequence mBusySequence; QTimeLine* mBusyAnimationTimeLine; bool mCreateThumbnailsForRemoteUrls; void setupBusyAnimation() { mBusySequence = KIconLoader::global()->loadPixmapSequence(QStringLiteral("process-working"), 22); mBusyAnimationTimeLine = new QTimeLine(100 * mBusySequence.frameCount(), q); mBusyAnimationTimeLine->setCurveShape(QTimeLine::LinearCurve); mBusyAnimationTimeLine->setEndFrame(mBusySequence.frameCount() - 1); mBusyAnimationTimeLine->setLoopCount(0); QObject::connect(mBusyAnimationTimeLine, &QTimeLine::frameChanged, q, &ThumbnailView::updateBusyIndexes); } void scheduleThumbnailGeneration() { if (mThumbnailProvider) { mThumbnailProvider->removePendingItems(); } mSmoothThumbnailQueue.clear(); mScheduledThumbnailGenerationTimer.start(); } void updateThumbnailForModifiedDocument(const QModelIndex& index) { Q_ASSERT(mDocumentInfoProvider); KFileItem item = fileItemForIndex(index); QUrl url = item.url(); ThumbnailGroup::Enum group = ThumbnailGroup::fromPixelSize(mThumbnailSize.width()); QPixmap pix; QSize fullSize; mDocumentInfoProvider->thumbnailForDocument(url, group, &pix, &fullSize); mThumbnailForUrl[url] = Thumbnail(QPersistentModelIndex(index), QDateTime::currentDateTime()); q->setThumbnail(item, pix, fullSize, 0); } void appendItemsToThumbnailProvider(const KFileItemList& list) { if (mThumbnailProvider) { ThumbnailGroup::Enum group = ThumbnailGroup::fromPixelSize(mThumbnailSize.width()); mThumbnailProvider->setThumbnailGroup(group); mThumbnailProvider->appendItems(list); } } void roughAdjustThumbnail(Thumbnail* thumbnail) { const QPixmap& mGroupPix = thumbnail->mGroupPix; const int groupSize = qMax(mGroupPix.width(), mGroupPix.height()); const int fullSize = qMax(thumbnail->mFullSize.width(), thumbnail->mFullSize.height()); if (fullSize == groupSize && mGroupPix.height() <= mThumbnailSize.height() && mGroupPix.width() <= mThumbnailSize.width()) { thumbnail->mAdjustedPix = mGroupPix; thumbnail->mRough = false; } else { thumbnail->mAdjustedPix = scale(mGroupPix, Qt::FastTransformation); thumbnail->mRough = true; } } void initDragPixmap(QDrag* drag, const QModelIndexList& indexes) { const int thumbCount = qMin(indexes.count(), int(DragPixmapGenerator::MaxCount)); QList lst; for (int row = 0; row < thumbCount; ++row) { const QUrl url = urlForIndex(indexes[row]); lst << mThumbnailForUrl.value(url).mAdjustedPix; } DragPixmapGenerator::DragPixmap dragPixmap = DragPixmapGenerator::generate(lst, indexes.count()); drag->setPixmap(dragPixmap.pix); drag->setHotSpot(dragPixmap.hotSpot); } QPixmap scale(const QPixmap& pix, Qt::TransformationMode transformationMode) { switch (mScaleMode) { case ThumbnailView::ScaleToFit: return pix.scaled(mThumbnailSize.width(), mThumbnailSize.height(), Qt::KeepAspectRatio, transformationMode); - break; case ThumbnailView::ScaleToSquare: { int minSize = qMin(pix.width(), pix.height()); QPixmap pix2 = pix.copy((pix.width() - minSize) / 2, (pix.height() - minSize) / 2, minSize, minSize); return pix2.scaled(mThumbnailSize.width(), mThumbnailSize.height(), Qt::KeepAspectRatio, transformationMode); } case ThumbnailView::ScaleToHeight: return pix.scaledToHeight(mThumbnailSize.height(), transformationMode); - break; case ThumbnailView::ScaleToWidth: return pix.scaledToWidth(mThumbnailSize.width(), transformationMode); - break; } // Keep compiler happy Q_ASSERT(0); return QPixmap(); } }; ThumbnailView::ThumbnailView(QWidget* parent) : QListView(parent) , d(new ThumbnailViewPrivate) { d->q = this; d->mScaleMode = ScaleToFit; - d->mThumbnailViewHelper = 0; - d->mDocumentInfoProvider = 0; - d->mThumbnailProvider = 0; + d->mThumbnailViewHelper = nullptr; + d->mDocumentInfoProvider = nullptr; + d->mThumbnailProvider = nullptr; // Init to some stupid value so that the first call to setThumbnailSize() // is not ignored (do not use 0 in case someone try to divide by // mThumbnailSize...) d->mThumbnailSize = QSize(1, 1); d->mThumbnailAspectRatio = 1; d->mCreateThumbnailsForRemoteUrls = true; setFrameShape(QFrame::NoFrame); setViewMode(QListView::IconMode); setResizeMode(QListView::Adjust); setDragEnabled(true); setAcceptDrops(true); setDropIndicatorShown(true); setUniformItemSizes(true); setEditTriggers(QAbstractItemView::EditKeyPressed); d->setupBusyAnimation(); setVerticalScrollMode(ScrollPerPixel); setHorizontalScrollMode(ScrollPerPixel); d->mScheduledThumbnailGenerationTimer.setSingleShot(true); d->mScheduledThumbnailGenerationTimer.setInterval(500); connect(&d->mScheduledThumbnailGenerationTimer, &QTimer::timeout, this, &ThumbnailView::generateThumbnailsForItems); d->mSmoothThumbnailTimer.setSingleShot(true); connect(&d->mSmoothThumbnailTimer, &QTimer::timeout, this, &ThumbnailView::smoothNextThumbnail); setContextMenuPolicy(Qt::CustomContextMenu); connect(this, &ThumbnailView::customContextMenuRequested, this, &ThumbnailView::showContextMenu); connect(this, &ThumbnailView::activated, this, &ThumbnailView::emitIndexActivatedIfNoModifiers); } ThumbnailView::~ThumbnailView() { delete d; } ThumbnailView::ThumbnailScaleMode ThumbnailView::thumbnailScaleMode() const { return d->mScaleMode; } void ThumbnailView::setThumbnailScaleMode(ThumbnailScaleMode mode) { d->mScaleMode = mode; setUniformItemSizes(mode == ScaleToFit || mode == ScaleToSquare); } void ThumbnailView::setModel(QAbstractItemModel* newModel) { if (model()) { - disconnect(model(), 0, this, 0); + disconnect(model(), nullptr, this, nullptr); } QListView::setModel(newModel); connect(model(), SIGNAL(rowsRemoved(QModelIndex,int,int)), SIGNAL(rowsRemovedSignal(QModelIndex,int,int))); } void ThumbnailView::setThumbnailProvider(ThumbnailProvider* thumbnailProvider) { GV_RETURN_IF_FAIL(d->mThumbnailProvider != thumbnailProvider); if (thumbnailProvider) { connect(thumbnailProvider, SIGNAL(thumbnailLoaded(KFileItem,QPixmap,QSize,qulonglong)), SLOT(setThumbnail(KFileItem,QPixmap,QSize,qulonglong))); connect(thumbnailProvider, SIGNAL(thumbnailLoadingFailed(KFileItem)), SLOT(setBrokenThumbnail(KFileItem))); } else { - disconnect(d->mThumbnailProvider, 0 , this, 0); + disconnect(d->mThumbnailProvider, nullptr , this, nullptr); } d->mThumbnailProvider = thumbnailProvider; } void ThumbnailView::updateThumbnailSize() { QSize value = d->mThumbnailSize; // mWaitingThumbnail int waitingThumbnailSize; if (value.width() > 64) { waitingThumbnailSize = 48; } else { waitingThumbnailSize = 32; } - QPixmap icon = DesktopIcon("chronometer", waitingThumbnailSize); + QPixmap icon = DesktopIcon(QStringLiteral("chronometer"), waitingThumbnailSize); QPixmap pix(value); pix.fill(Qt::transparent); QPainter painter(&pix); painter.setOpacity(0.5); painter.drawPixmap((value.width() - icon.width()) / 2, (value.height() - icon.height()) / 2, icon); painter.end(); d->mWaitingThumbnail = pix; // Stop smoothing d->mSmoothThumbnailTimer.stop(); d->mSmoothThumbnailQueue.clear(); // Clear adjustedPixes ThumbnailForUrl::iterator it = d->mThumbnailForUrl.begin(), end = d->mThumbnailForUrl.end(); for (; it != end; ++it) { it.value().mAdjustedPix = QPixmap(); } thumbnailSizeChanged(value); thumbnailWidthChanged(value.width()); if (d->mScaleMode != ScaleToFit) { scheduleDelayedItemsLayout(); } d->scheduleThumbnailGeneration(); } void ThumbnailView::setThumbnailWidth(int width) { if(d->mThumbnailSize.width() == width) { return; } int height = round((qreal)width / d->mThumbnailAspectRatio); d->mThumbnailSize = QSize(width, height); updateThumbnailSize(); } void ThumbnailView::setThumbnailAspectRatio(qreal ratio) { if(d->mThumbnailAspectRatio == ratio) { return; } d->mThumbnailAspectRatio = ratio; int width = d->mThumbnailSize.width(); int height = round((qreal)width / d->mThumbnailAspectRatio); d->mThumbnailSize = QSize(width, height); updateThumbnailSize(); } qreal ThumbnailView::thumbnailAspectRatio() const { return d->mThumbnailAspectRatio; } QSize ThumbnailView::thumbnailSize() const { return d->mThumbnailSize; } void ThumbnailView::setThumbnailViewHelper(AbstractThumbnailViewHelper* helper) { d->mThumbnailViewHelper = helper; } AbstractThumbnailViewHelper* ThumbnailView::thumbnailViewHelper() const { return d->mThumbnailViewHelper; } void ThumbnailView::setDocumentInfoProvider(AbstractDocumentInfoProvider* provider) { d->mDocumentInfoProvider = provider; if (provider) { connect(provider, &AbstractDocumentInfoProvider::busyStateChanged, this, &ThumbnailView::updateThumbnailBusyState); connect(provider, &AbstractDocumentInfoProvider::documentChanged, this, &ThumbnailView::updateThumbnail); } } AbstractDocumentInfoProvider* ThumbnailView::documentInfoProvider() const { return d->mDocumentInfoProvider; } void ThumbnailView::rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) { QListView::rowsAboutToBeRemoved(parent, start, end); // Remove references to removed items KFileItemList itemList; for (int pos = start; pos <= end; ++pos) { QModelIndex index = model()->index(pos, 0, parent); KFileItem item = fileItemForIndex(index); if (item.isNull()) { //qDebug() << "Skipping invalid item!" << index.data().toString(); continue; } QUrl url = item.url(); d->mThumbnailForUrl.remove(url); d->mSmoothThumbnailQueue.removeAll(url); itemList.append(item); } if (d->mThumbnailProvider) { d->mThumbnailProvider->removeItems(itemList); } // Removing rows might make new images visible, make sure their thumbnail // is generated d->mScheduledThumbnailGenerationTimer.start(); } void ThumbnailView::rowsInserted(const QModelIndex& parent, int start, int end) { QListView::rowsInserted(parent, start, end); d->mScheduledThumbnailGenerationTimer.start(); rowsInsertedSignal(parent, start, end); } void ThumbnailView::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector &roles) { QListView::dataChanged(topLeft, bottomRight, roles); bool thumbnailsNeedRefresh = false; for (int row = topLeft.row(); row <= bottomRight.row(); ++row) { QModelIndex index = model()->index(row, 0); KFileItem item = fileItemForIndex(index); if (item.isNull()) { qWarning() << "Invalid item for index" << index << ". This should not happen!"; GV_FATAL_FAILS; continue; } ThumbnailForUrl::Iterator it = d->mThumbnailForUrl.find(item.url()); if (it != d->mThumbnailForUrl.end()) { // All thumbnail views are connected to the model, so // ThumbnailView::dataChanged() is called for all of them. As a // result this method will also be called for views which are not // currently visible, and do not yet have a thumbnail for the // modified url. QDateTime mtime = item.time(KFileItem::ModificationTime); if (it->mModificationTime != mtime || it->mFileSize != item.size()) { // dataChanged() is called when the file changes but also when // the model fetched additional data such as semantic info. To // avoid needless refreshes, we only trigger a refresh if the // modification time changes. thumbnailsNeedRefresh = true; it->prepareForRefresh(mtime); } } } if (thumbnailsNeedRefresh) { d->mScheduledThumbnailGenerationTimer.start(); } } void ThumbnailView::showContextMenu() { d->mThumbnailViewHelper->showContextMenu(this); } void ThumbnailView::emitIndexActivatedIfNoModifiers(const QModelIndex& index) { if (QApplication::keyboardModifiers() == Qt::NoModifier) { emit indexActivated(index); } } void ThumbnailView::setThumbnail(const KFileItem& item, const QPixmap& pixmap, const QSize& size, qulonglong fileSize) { ThumbnailForUrl::iterator it = d->mThumbnailForUrl.find(item.url()); if (it == d->mThumbnailForUrl.end()) { return; } Thumbnail& thumbnail = it.value(); thumbnail.mGroupPix = pixmap; thumbnail.mAdjustedPix = QPixmap(); int largeGroupSize = ThumbnailGroup::pixelSize(ThumbnailGroup::Large); thumbnail.mFullSize = size.isValid() ? size : QSize(largeGroupSize, largeGroupSize); thumbnail.mRealFullSize = size; thumbnail.mWaitingForThumbnail = false; thumbnail.mFileSize = fileSize; update(thumbnail.mIndex); if (d->mScaleMode != ScaleToFit) { scheduleDelayedItemsLayout(); } } void ThumbnailView::setBrokenThumbnail(const KFileItem& item) { ThumbnailForUrl::iterator it = d->mThumbnailForUrl.find(item.url()); if (it == d->mThumbnailForUrl.end()) { return; } Thumbnail& thumbnail = it.value(); MimeTypeUtils::Kind kind = MimeTypeUtils::fileItemKind(item); if (kind == MimeTypeUtils::KIND_VIDEO) { // Special case for videos because our kde install may come without // support for video thumbnails so we show the mimetype icon instead of // a broken image icon const QPixmap pix = KIconLoader::global()->loadIcon(item.iconName(), KIconLoader::Desktop, d->mThumbnailSize.height()); thumbnail.initAsIcon(pix); } else if (kind == MimeTypeUtils::KIND_DIR) { // Special case for folders because ThumbnailProvider does not return a // thumbnail if there is no images thumbnail.mWaitingForThumbnail = false; return; } else { - thumbnail.initAsIcon(DesktopIcon("image-missing", 48)); + thumbnail.initAsIcon(DesktopIcon(QStringLiteral("image-missing"), 48)); thumbnail.mFullSize = thumbnail.mGroupPix.size(); } update(thumbnail.mIndex); } QPixmap ThumbnailView::thumbnailForIndex(const QModelIndex& index, QSize* fullSize) { KFileItem item = fileItemForIndex(index); if (item.isNull()) { LOG("Invalid item"); if (fullSize) { *fullSize = QSize(); } return QPixmap(); } QUrl url = item.url(); // Find or create Thumbnail instance ThumbnailForUrl::Iterator it = d->mThumbnailForUrl.find(url); if (it == d->mThumbnailForUrl.end()) { Thumbnail thumbnail = Thumbnail(QPersistentModelIndex(index), item.time(KFileItem::ModificationTime)); it = d->mThumbnailForUrl.insert(url, thumbnail); } Thumbnail& thumbnail = it.value(); // If dir or archive, generate a thumbnail from fileitem pixmap MimeTypeUtils::Kind kind = MimeTypeUtils::fileItemKind(item); if (kind == MimeTypeUtils::KIND_ARCHIVE || kind == MimeTypeUtils::KIND_DIR) { int groupSize = ThumbnailGroup::pixelSize(ThumbnailGroup::fromPixelSize(d->mThumbnailSize.height())); if (thumbnail.mGroupPix.isNull() || thumbnail.mGroupPix.height() < groupSize) { const QPixmap pix = KIconLoader::global()->loadIcon(item.iconName(), KIconLoader::Desktop, d->mThumbnailSize.height()); thumbnail.initAsIcon(pix); if (kind == MimeTypeUtils::KIND_ARCHIVE) { // No thumbnails for archives thumbnail.mWaitingForThumbnail = false; } else if (!d->mCreateThumbnailsForRemoteUrls && !UrlUtils::urlIsFastLocalFile(url)) { // If we don't want thumbnails for remote urls, use // "folder-remote" icon for remote folders, so that they do // not look like regular folders thumbnail.mWaitingForThumbnail = false; - thumbnail.initAsIcon(DesktopIcon("folder-remote", groupSize)); + thumbnail.initAsIcon(DesktopIcon(QStringLiteral("folder-remote"), groupSize)); } else { // set mWaitingForThumbnail to true (necessary in the case // 'thumbnail' already existed before, but with a too small // mGroupPix) thumbnail.mWaitingForThumbnail = true; } } } if (thumbnail.mGroupPix.isNull()) { if (fullSize) { *fullSize = QSize(); } return d->mWaitingThumbnail; } // Adjust thumbnail if (thumbnail.mAdjustedPix.isNull()) { d->roughAdjustThumbnail(&thumbnail); } if (thumbnail.mRough && !d->mSmoothThumbnailQueue.contains(url)) { d->mSmoothThumbnailQueue.enqueue(url); if (!d->mSmoothThumbnailTimer.isActive()) { d->mSmoothThumbnailTimer.start(SMOOTH_DELAY); } } if (fullSize) { *fullSize = thumbnail.mRealFullSize; } return thumbnail.mAdjustedPix; } bool ThumbnailView::isModified(const QModelIndex& index) const { if (!d->mDocumentInfoProvider) { return false; } QUrl url = urlForIndex(index); return d->mDocumentInfoProvider->isModified(url); } bool ThumbnailView::isBusy(const QModelIndex& index) const { if (!d->mDocumentInfoProvider) { return false; } QUrl url = urlForIndex(index); return d->mDocumentInfoProvider->isBusy(url); } void ThumbnailView::startDrag(Qt::DropActions) { const QModelIndexList indexes = selectionModel()->selectedIndexes(); if (indexes.isEmpty()) { return; } KFileItemList selectedFiles; - for (const auto index : indexes) { + for (const auto &index : indexes) { selectedFiles << fileItemForIndex(index); } QDrag* drag = new QDrag(this); drag->setMimeData(MimeTypeUtils::selectionMimeData(selectedFiles)); d->initDragPixmap(drag, indexes); drag->exec(Qt::MoveAction | Qt::CopyAction | Qt::LinkAction, Qt::CopyAction); } void ThumbnailView::dragEnterEvent(QDragEnterEvent* event) { QAbstractItemView::dragEnterEvent(event); if (event->mimeData()->hasUrls()) { event->acceptProposedAction(); } } void ThumbnailView::dragMoveEvent(QDragMoveEvent* event) { // Necessary, otherwise we don't reach dropEvent() QAbstractItemView::dragMoveEvent(event); event->acceptProposedAction(); } void ThumbnailView::dropEvent(QDropEvent* event) { const QList urlList = KUrlMimeData::urlsFromMimeData(event->mimeData()); if (urlList.isEmpty()) { return; } QModelIndex destIndex = indexAt(event->pos()); if (destIndex.isValid()) { KFileItem item = fileItemForIndex(destIndex); if (item.isDir()) { QUrl destUrl = item.url(); d->mThumbnailViewHelper->showMenuForUrlDroppedOnDir(this, urlList, destUrl); return; } } d->mThumbnailViewHelper->showMenuForUrlDroppedOnViewport(this, urlList); event->acceptProposedAction(); } void ThumbnailView::keyPressEvent(QKeyEvent* event) { QListView::keyPressEvent(event); if (event->key() == Qt::Key_Return) { const QModelIndex index = selectionModel()->currentIndex(); if (index.isValid() && selectionModel()->selectedIndexes().count() == 1) { emit indexActivated(index); } } } void ThumbnailView::resizeEvent(QResizeEvent* event) { QListView::resizeEvent(event); d->scheduleThumbnailGeneration(); } void ThumbnailView::showEvent(QShowEvent* event) { QListView::showEvent(event); d->scheduleThumbnailGeneration(); QTimer::singleShot(0, this, SLOT(scrollToSelectedIndex())); } void ThumbnailView::wheelEvent(QWheelEvent* event) { // If we don't adjust the single step, the wheel scroll exactly one item up // and down, giving the impression that the items do not move but only // their label changes. // For some reason it is necessary to set the step here: setting it in // setThumbnailSize() does not work //verticalScrollBar()->setSingleStep(d->mThumbnailSize / 5); if (event->modifiers() == Qt::ControlModifier) { int width = d->mThumbnailSize.width() + (event->delta() > 0 ? 1 : -1) * WHEEL_ZOOM_MULTIPLIER; width = qMax(int(MinThumbnailSize), qMin(width, int(MaxThumbnailSize))); setThumbnailWidth(width); } else { QListView::wheelEvent(event); } } void ThumbnailView::scrollToSelectedIndex() { QModelIndexList list = selectedIndexes(); if (list.count() >= 1) { scrollTo(list.first(), PositionAtCenter); } } void ThumbnailView::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) { QListView::selectionChanged(selected, deselected); emit selectionChangedSignal(selected, deselected); } void ThumbnailView::scrollContentsBy(int dx, int dy) { QListView::scrollContentsBy(dx, dy); d->scheduleThumbnailGeneration(); } void ThumbnailView::generateThumbnailsForItems() { if (!isVisible() || !model()) { return; } const QRect visibleRect = viewport()->rect(); const int visibleSurface = visibleRect.width() * visibleRect.height(); const QPoint origin = visibleRect.center(); // distance => item QMultiMap itemMap; for (int row = 0; row < model()->rowCount(); ++row) { QModelIndex index = model()->index(row, 0); KFileItem item = fileItemForIndex(index); QUrl url = item.url(); // Filter out remote items if necessary if (!d->mCreateThumbnailsForRemoteUrls && !url.isLocalFile()) { continue; } // Filter out archives MimeTypeUtils::Kind kind = MimeTypeUtils::fileItemKind(item); if (kind == MimeTypeUtils::KIND_ARCHIVE) { continue; } // Immediately update modified items if (d->mDocumentInfoProvider && d->mDocumentInfoProvider->isModified(url)) { d->updateThumbnailForModifiedDocument(index); continue; } // Filter out items which already have a thumbnail ThumbnailForUrl::ConstIterator it = d->mThumbnailForUrl.constFind(url); if (it != d->mThumbnailForUrl.constEnd() && it.value().isGroupPixAdaptedForSize(d->mThumbnailSize.height())) { continue; } // Compute distance int distance; const QRect itemRect = visualRect(index); const qreal itemSurface = itemRect.width() * itemRect.height(); const QRect visibleItemRect = visibleRect.intersected(itemRect); qreal visibleItemFract = 0; if (itemSurface > 0) { visibleItemFract = visibleItemRect.width() * visibleItemRect.height() / itemSurface; } if (visibleItemFract > 0.7) { // Item is visible, order thumbnails from left to right, top to bottom // Distance is computed so that it is between 0 and visibleSurface distance = itemRect.top() * visibleRect.width() + itemRect.left(); // Make sure directory thumbnails are generated after image thumbnails: // Distance is between visibleSurface and 2 * visibleSurface if (kind == MimeTypeUtils::KIND_DIR) { distance = distance + visibleSurface; } } else { // Item is not visible, order thumbnails according to distance // Start at 2 * visibleSurface to ensure invisible thumbnails are // generated *after* visible thumbnails distance = 2 * visibleSurface + (itemRect.center() - origin).manhattanLength(); } // Add the item to our map itemMap.insert(distance, item); // Insert the thumbnail in mThumbnailForUrl, so that // setThumbnail() can find the item to update if (it == d->mThumbnailForUrl.constEnd()) { Thumbnail thumbnail = Thumbnail(QPersistentModelIndex(index), item.time(KFileItem::ModificationTime)); d->mThumbnailForUrl.insert(url, thumbnail); } } if (!itemMap.isEmpty()) { d->appendItemsToThumbnailProvider(itemMap.values()); } } void ThumbnailView::updateThumbnail(const QUrl& url) { const ThumbnailForUrl::Iterator it = d->mThumbnailForUrl.find(url); if (it == d->mThumbnailForUrl.end()) { return; } if (d->mDocumentInfoProvider) { d->updateThumbnailForModifiedDocument(it->mIndex); } else { const KFileItem item = fileItemForIndex(it->mIndex); d->appendItemsToThumbnailProvider(KFileItemList({ item })); } } void ThumbnailView::updateThumbnailBusyState(const QUrl& url, bool busy) { const ThumbnailForUrl::Iterator it = d->mThumbnailForUrl.find(url); if (it == d->mThumbnailForUrl.end()) { return; } QPersistentModelIndex index(it->mIndex); if (busy && !d->mBusyIndexSet.contains(index)) { d->mBusyIndexSet << index; update(index); if (d->mBusyAnimationTimeLine->state() != QTimeLine::Running) { d->mBusyAnimationTimeLine->start(); } } else if (!busy && d->mBusyIndexSet.remove(index)) { update(index); if (d->mBusyIndexSet.isEmpty()) { d->mBusyAnimationTimeLine->stop(); } } } void ThumbnailView::updateBusyIndexes() { Q_FOREACH(const QPersistentModelIndex & index, d->mBusyIndexSet) { update(index); } } QPixmap ThumbnailView::busySequenceCurrentPixmap() const { return d->mBusySequence.frameAt(d->mBusyAnimationTimeLine->currentFrame()); } void ThumbnailView::smoothNextThumbnail() { if (d->mSmoothThumbnailQueue.isEmpty()) { return; } if (d->mThumbnailProvider && d->mThumbnailProvider->isRunning()) { // give mThumbnailProvider priority over smoothing d->mSmoothThumbnailTimer.start(SMOOTH_DELAY); return; } QUrl url = d->mSmoothThumbnailQueue.dequeue(); ThumbnailForUrl::Iterator it = d->mThumbnailForUrl.find(url); GV_RETURN_IF_FAIL2(it != d->mThumbnailForUrl.end(), url << "not in mThumbnailForUrl."); Thumbnail& thumbnail = it.value(); thumbnail.mAdjustedPix = d->scale(thumbnail.mGroupPix, Qt::SmoothTransformation); thumbnail.mRough = false; GV_RETURN_IF_FAIL2(thumbnail.mIndex.isValid(), "index for" << url << "is invalid."); update(thumbnail.mIndex); if (!d->mSmoothThumbnailQueue.isEmpty()) { d->mSmoothThumbnailTimer.start(0); } } void ThumbnailView::reloadThumbnail(const QModelIndex& index) { QUrl url = urlForIndex(index); if (!url.isValid()) { qWarning() << "Invalid url for index" << index; return; } ThumbnailProvider::deleteImageThumbnail(url); ThumbnailForUrl::Iterator it = d->mThumbnailForUrl.find(url); if (it == d->mThumbnailForUrl.end()) { return; } d->mThumbnailForUrl.erase(it); generateThumbnailsForItems(); } void ThumbnailView::setCreateThumbnailsForRemoteUrls(bool createRemoteThumbs) { d->mCreateThumbnailsForRemoteUrls = createRemoteThumbs; } } // namespace diff --git a/lib/thumbnailview/thumbnailview.h b/lib/thumbnailview/thumbnailview.h index 1de1ff87..d6027990 100644 --- a/lib/thumbnailview/thumbnailview.h +++ b/lib/thumbnailview/thumbnailview.h @@ -1,216 +1,216 @@ /* 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. */ #ifndef THUMBNAILVIEW_H #define THUMBNAILVIEW_H #include // Qt #include // KDE #include class KFileItem; class QDragEnterEvent; class QDragMoveEvent; class QDropEvent; class QPixmap; namespace Gwenview { class AbstractDocumentInfoProvider; class AbstractThumbnailViewHelper; class ThumbnailProvider; struct ThumbnailViewPrivate; class GWENVIEWLIB_EXPORT ThumbnailView : public QListView { Q_OBJECT public: enum { MinThumbnailSize = 48, MaxThumbnailSize = 256 }; enum ThumbnailScaleMode { ScaleToSquare, ScaleToHeight, ScaleToWidth, ScaleToFit }; explicit ThumbnailView(QWidget* parent); - ~ThumbnailView() Q_DECL_OVERRIDE; + ~ThumbnailView() override; void setThumbnailViewHelper(AbstractThumbnailViewHelper* helper); AbstractThumbnailViewHelper* thumbnailViewHelper() const; void setDocumentInfoProvider(AbstractDocumentInfoProvider* provider); AbstractDocumentInfoProvider* documentInfoProvider() const; ThumbnailScaleMode thumbnailScaleMode() const; void setThumbnailScaleMode(ThumbnailScaleMode); /** * Returns the thumbnail size. */ QSize thumbnailSize() const; /** * Returns the aspect ratio of the thumbnail. */ qreal thumbnailAspectRatio() const; QPixmap thumbnailForIndex(const QModelIndex&, QSize* fullSize = nullptr); /** * Returns true if the document pointed by the index has been modified * inside Gwenview. */ bool isModified(const QModelIndex&) const; /** * Returns true if the document pointed by the index is currently busy * (loading, saving, rotating...) */ bool isBusy(const QModelIndex& index) const; - void setModel(QAbstractItemModel* model) Q_DECL_OVERRIDE; + void setModel(QAbstractItemModel* model) override; void setThumbnailProvider(ThumbnailProvider* thumbnailProvider); /** * Publish this method so that delegates can call it. */ using QListView::scheduleDelayedItemsLayout; /** * Returns the current pixmap to paint when drawing a busy index. */ QPixmap busySequenceCurrentPixmap() const; void reloadThumbnail(const QModelIndex&); void updateThumbnailSize(); void setCreateThumbnailsForRemoteUrls(bool createRemoteThumbs); Q_SIGNALS: /** * It seems we can't use the 'activated()' signal for now because it does * not know about KDE single vs doubleclick settings. The indexActivated() * signal replaces it. */ void indexActivated(const QModelIndex&); void urlListDropped(const QList& lst, const QUrl &destination); void thumbnailSizeChanged(const QSize&); void thumbnailWidthChanged(int); /** * Emitted whenever selectionChanged() is called. * This signal is suffixed with "Signal" because * QAbstractItemView::selectionChanged() is a slot. */ void selectionChangedSignal(const QItemSelection&, const QItemSelection&); /** * Forward some signals from model, so that the delegate can use them */ void rowsRemovedSignal(const QModelIndex& parent, int start, int end); void rowsInsertedSignal(const QModelIndex& parent, int start, int end); public Q_SLOTS: /** * Sets the thumbnail's width, in pixels. Keeps aspect ratio unchanged. */ void setThumbnailWidth(int width); /** * Sets the thumbnail's aspect ratio. Keeps width unchanged. */ void setThumbnailAspectRatio(qreal ratio); void scrollToSelectedIndex(); void generateThumbnailsForItems(); protected: - void dragEnterEvent(QDragEnterEvent*) Q_DECL_OVERRIDE; + void dragEnterEvent(QDragEnterEvent*) override; - void dragMoveEvent(QDragMoveEvent*) Q_DECL_OVERRIDE; + void dragMoveEvent(QDragMoveEvent*) override; - void dropEvent(QDropEvent*) Q_DECL_OVERRIDE; + void dropEvent(QDropEvent*) override; - void keyPressEvent(QKeyEvent*) Q_DECL_OVERRIDE; + void keyPressEvent(QKeyEvent*) override; - void resizeEvent(QResizeEvent*) Q_DECL_OVERRIDE; + void resizeEvent(QResizeEvent*) override; - void scrollContentsBy(int dx, int dy) Q_DECL_OVERRIDE; + void scrollContentsBy(int dx, int dy) override; - void showEvent(QShowEvent*) Q_DECL_OVERRIDE; + void showEvent(QShowEvent*) override; - void wheelEvent(QWheelEvent*) Q_DECL_OVERRIDE; + void wheelEvent(QWheelEvent*) override; - void startDrag(Qt::DropActions) Q_DECL_OVERRIDE; + void startDrag(Qt::DropActions) override; protected Q_SLOTS: - void rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) Q_DECL_OVERRIDE; - void rowsInserted(const QModelIndex& parent, int start, int end) Q_DECL_OVERRIDE; - void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) Q_DECL_OVERRIDE; + void rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) override; + void rowsInserted(const QModelIndex& parent, int start, int end) override; + void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) override; virtual void dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, - const QVector &roles = QVector()) Q_DECL_OVERRIDE; + const QVector &roles = QVector()) override; private Q_SLOTS: void showContextMenu(); void emitIndexActivatedIfNoModifiers(const QModelIndex&); void setThumbnail(const KFileItem&, const QPixmap&, const QSize&, qulonglong fileSize); void setBrokenThumbnail(const KFileItem&); /** * Generate thumbnail for url. */ void updateThumbnail(const QUrl& url); /** * Tells the view the busy state of the document pointed by the url has changed. */ void updateThumbnailBusyState(const QUrl& url, bool); /* * Cause a repaint of all busy indexes */ void updateBusyIndexes(); void smoothNextThumbnail(); private: friend struct ThumbnailViewPrivate; ThumbnailViewPrivate * const d; }; } // namespace #endif /* THUMBNAILVIEW_H */ diff --git a/lib/thumbnailview/tooltipwidget.h b/lib/thumbnailview/tooltipwidget.h index 63ae825b..c178a793 100644 --- a/lib/thumbnailview/tooltipwidget.h +++ b/lib/thumbnailview/tooltipwidget.h @@ -1,64 +1,64 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2009 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, Cambridge, MA 02110-1301, USA. */ #ifndef TOOLTIPWIDGET_H #define TOOLTIPWIDGET_H // Qt #include // KDE // Local namespace Gwenview { struct ToolTipWidgetPrivate; /** * A label which uses tooltip colors and can be faded */ class ToolTipWidget : public QWidget { Q_OBJECT Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity) public: explicit ToolTipWidget(QWidget* parent = nullptr); - ~ToolTipWidget() Q_DECL_OVERRIDE; + ~ToolTipWidget() override; qreal opacity() const; void setOpacity(qreal); QString text() const; void setText(const QString& text); - QSize sizeHint() const Q_DECL_OVERRIDE; + QSize sizeHint() const override; protected: - void paintEvent(QPaintEvent*) Q_DECL_OVERRIDE; + void paintEvent(QPaintEvent*) override; private: ToolTipWidgetPrivate* const d; }; } // namespace #endif /* TOOLTIPWIDGET_H */ diff --git a/lib/timeutils.cpp b/lib/timeutils.cpp index 1fcecc98..9e8836a9 100644 --- a/lib/timeutils.cpp +++ b/lib/timeutils.cpp @@ -1,150 +1,150 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 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, Cambridge, MA 02110-1301, USA. */ // Self #include "timeutils.h" // Qt #include #include #include // KDE #include // Exiv2 #include #include // Local #include #include namespace Gwenview { namespace TimeUtils { static Exiv2::ExifData::const_iterator findDateTimeKey(const Exiv2::ExifData& exifData) { // Ordered list of keys to try static QList lst = QList() << Exiv2::ExifKey("Exif.Photo.DateTimeOriginal") << Exiv2::ExifKey("Exif.Image.DateTimeOriginal") << Exiv2::ExifKey("Exif.Photo.DateTimeDigitized") << Exiv2::ExifKey("Exif.Image.DateTime"); Exiv2::ExifData::const_iterator it, end = exifData.end(); Q_FOREACH(const Exiv2::ExifKey& key, lst) { it = exifData.findKey(key); if (it != end) { return it; } } return end; } struct CacheItem { QDateTime fileMTime; QDateTime realTime; void update(const KFileItem& fileItem) { QDateTime time = fileItem.time(KFileItem::ModificationTime); if (fileMTime == time) { return; } fileMTime = time; if (!updateFromExif(fileItem.url())) { realTime = time; } } bool updateFromExif(const QUrl &url) { if (!UrlUtils::urlIsFastLocalFile(url)) { return false; } QString path = url.path(); Exiv2ImageLoader loader; if (!loader.load(path)) { return false; } Exiv2::Image::AutoPtr img = loader.popImage(); try { Exiv2::ExifData exifData = img->exifData(); if (exifData.empty()) { return false; } Exiv2::ExifData::const_iterator it = findDateTimeKey(exifData); if (it == exifData.end()) { qWarning() << "No date in exif header of" << path; return false; } std::ostringstream stream; stream << *it; QString value = QString::fromLocal8Bit(stream.str().c_str()); - QDateTime dt = QDateTime::fromString(value, "yyyy:MM:dd hh:mm:ss"); + QDateTime dt = QDateTime::fromString(value, QStringLiteral("yyyy:MM:dd hh:mm:ss")); if (!dt.isValid()) { qWarning() << "Invalid date in exif header of" << path; return false; } realTime = dt; return true; } catch (const Exiv2::Error& error) { qWarning() << "Failed to read date from exif header of" << path << ". Error:" << error.what(); return false; } } }; typedef QHash Cache; QDateTime dateTimeForFileItem(const KFileItem& fileItem, CachePolicy cachePolicy) { if (cachePolicy == SkipCache) { CacheItem item; item.update(fileItem); return item.realTime; } static Cache cache; const QUrl url = fileItem.targetUrl(); Cache::iterator it = cache.find(url); if (it == cache.end()) { it = cache.insert(url, CacheItem()); } it.value().update(fileItem); return it.value().realTime; } } // namespace } // namespace diff --git a/lib/transformimageoperation.h b/lib/transformimageoperation.h index 3b853803..ed34b95e 100644 --- a/lib/transformimageoperation.h +++ b/lib/transformimageoperation.h @@ -1,66 +1,66 @@ // 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. */ #ifndef TRANSFORMIMAGEOPERATION_H #define TRANSFORMIMAGEOPERATION_H #include // Qt // KDE // Local #include #include #include namespace Gwenview { class TransformJob : public ThreadedDocumentJob { Q_OBJECT public: TransformJob(Orientation orientation); - void threadedStart() Q_DECL_OVERRIDE; + void threadedStart() override; private: Orientation mOrientation; }; struct TransformImageOperationPrivate; class GWENVIEWLIB_EXPORT TransformImageOperation : public AbstractImageOperation { public: TransformImageOperation(Orientation); - ~TransformImageOperation() Q_DECL_OVERRIDE; + ~TransformImageOperation() override; - void redo() Q_DECL_OVERRIDE; - void undo() Q_DECL_OVERRIDE; + void redo() override; + void undo() override; private: TransformImageOperationPrivate* const d; }; } // namespace #endif /* TRANSFORMIMAGEOPERATION_H */ diff --git a/lib/widgetfloater.h b/lib/widgetfloater.h index 1411b848..3c85c860 100644 --- a/lib/widgetfloater.h +++ b/lib/widgetfloater.h @@ -1,69 +1,69 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 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, Cambridge, MA 02110-1301, USA. */ #ifndef WIDGETFLOATER_H #define WIDGETFLOATER_H #include // Qt #include // KDE // Local namespace Gwenview { struct WidgetFloaterPrivate; /** * This helper object makes it possible to place a widget (the child) over * another (the parent), ensuring the child remains aligned as specified by * setAlignment() whenever either widget get resized. */ class GWENVIEWLIB_EXPORT WidgetFloater : public QObject { Q_OBJECT public: explicit WidgetFloater(QWidget* parent); - ~WidgetFloater() Q_DECL_OVERRIDE; + ~WidgetFloater() override; void setChildWidget(QWidget*); void setAlignment(Qt::Alignment); void setHorizontalMargin(int); int horizontalMargin() const; void setVerticalMargin(int); int verticalMargin() const; protected: - bool eventFilter(QObject*, QEvent*) Q_DECL_OVERRIDE; + bool eventFilter(QObject*, QEvent*) override; private: WidgetFloaterPrivate* const d; }; } // namespace #endif /* WIDGETFLOATER_H */ diff --git a/lib/zoomslider.cpp b/lib/zoomslider.cpp index dec40e7d..246f5c16 100644 --- a/lib/zoomslider.cpp +++ b/lib/zoomslider.cpp @@ -1,157 +1,157 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2009 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, Cambridge, MA 02110-1301, USA. */ // Self #include "zoomslider.h" // Qt #include #include #include #include #include // KDE // Local namespace Gwenview { struct ZoomSliderPrivate { QToolButton* mZoomOutButton; QToolButton* mZoomInButton; QSlider* mSlider; QAction* mZoomInAction; QAction* mZoomOutAction; void updateButtons() { // Use QSlider::sliderPosition(), not QSlider::value() because when we are // called from slotZoomSliderActionTriggered(), QSlider::value() has not // been updated yet. mZoomOutButton->setEnabled(mSlider->sliderPosition() > mSlider->minimum()); mZoomInButton->setEnabled(mSlider->sliderPosition() < mSlider->maximum()); } }; -static QToolButton* createZoomButton(const char* iconName) +static QToolButton* createZoomButton(const QString &iconName) { QToolButton* button = new QToolButton; button->setIcon(QIcon::fromTheme(iconName)); button->setAutoRaise(true); button->setAutoRepeat(true); return button; } ZoomSlider::ZoomSlider(QWidget* parent) : QWidget(parent) , d(new ZoomSliderPrivate) { - d->mZoomInButton = createZoomButton("zoom-in"); - d->mZoomOutButton = createZoomButton("zoom-out"); + d->mZoomInButton = createZoomButton(QStringLiteral("zoom-in")); + d->mZoomOutButton = createZoomButton(QStringLiteral("zoom-out")); d->mZoomInAction = nullptr; d->mZoomOutAction = nullptr; d->mSlider = new QSlider; d->mSlider->setOrientation(Qt::Horizontal); QHBoxLayout* layout = new QHBoxLayout(this); layout->setMargin(0); layout->setSpacing(0); layout->addWidget(d->mZoomOutButton); layout->addWidget(d->mSlider); layout->addWidget(d->mZoomInButton); connect(d->mSlider, &QSlider::actionTriggered, this, &ZoomSlider::slotActionTriggered); connect(d->mSlider, &QSlider::valueChanged, this, &ZoomSlider::valueChanged); connect(d->mZoomOutButton, &QToolButton::clicked, this, &ZoomSlider::zoomOut); connect(d->mZoomInButton, &QToolButton::clicked, this, &ZoomSlider::zoomIn); } ZoomSlider::~ZoomSlider() { delete d; } int ZoomSlider::value() const { return d->mSlider->value(); } void ZoomSlider::setValue(int value) { d->mSlider->setValue(value); d->updateButtons(); } void ZoomSlider::setMinimum(int value) { d->mSlider->setMinimum(value); d->updateButtons(); } void ZoomSlider::setMaximum(int value) { d->mSlider->setMaximum(value); d->updateButtons(); } void ZoomSlider::setZoomInAction(QAction* action) { d->mZoomInAction = action; } void ZoomSlider::setZoomOutAction(QAction* action) { d->mZoomOutAction = action; } void ZoomSlider::slotActionTriggered(int) { d->updateButtons(); } void ZoomSlider::zoomOut() { if (d->mZoomOutAction) { d->mZoomOutAction->trigger(); } else { d->mSlider->triggerAction(QAbstractSlider::SliderPageStepSub); } } void ZoomSlider::zoomIn() { if (d->mZoomInAction) { d->mZoomInAction->trigger(); } else { d->mSlider->triggerAction(QAbstractSlider::SliderPageStepAdd); } } QSlider* ZoomSlider::slider() const { return d->mSlider; } } // namespace diff --git a/lib/zoomwidget.cpp b/lib/zoomwidget.cpp index 6b9102f0..2de6cd47 100644 --- a/lib/zoomwidget.cpp +++ b/lib/zoomwidget.cpp @@ -1,201 +1,201 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2008 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, Cambridge, MA 02110-1301, USA. */ // Self #include "zoomwidget.h" // stdc++ #include // Qt #include #include #include #include #include // KDE // Local #include "zoomslider.h" #include "signalblocker.h" #include "statusbartoolbutton.h" namespace Gwenview { static const qreal MAGIC_K = 1.04; static const qreal MAGIC_OFFSET = 16.; static const qreal PRECISION = 100.; inline int sliderValueForZoom(qreal zoom) { return int(PRECISION * (log(zoom) / log(MAGIC_K) + MAGIC_OFFSET)); } inline qreal zoomForSliderValue(int sliderValue) { return pow(MAGIC_K, sliderValue / PRECISION - MAGIC_OFFSET); } struct ZoomWidgetPrivate { ZoomWidget* q; StatusBarToolButton* mZoomToFitButton; StatusBarToolButton* mActualSizeButton; StatusBarToolButton* mZoomToFillButton; QLabel* mZoomLabel; ZoomSlider* mZoomSlider; QAction* mZoomToFitAction; QAction* mActualSizeAction; QAction* mZoomToFillAction; bool mZoomUpdatedBySlider; void emitZoomChanged() { // Use QSlider::sliderPosition(), not QSlider::value() because when we are // called from slotZoomSliderActionTriggered(), QSlider::value() has not // been updated yet. qreal zoom = zoomForSliderValue(mZoomSlider->slider()->sliderPosition()); mZoomUpdatedBySlider = true; emit q->zoomChanged(zoom); mZoomUpdatedBySlider = false; } }; ZoomWidget::ZoomWidget(QWidget* parent) : QFrame(parent) , d(new ZoomWidgetPrivate) { d->q = this; d->mZoomUpdatedBySlider = false; setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum); d->mZoomToFitButton = new StatusBarToolButton; d->mActualSizeButton = new StatusBarToolButton; d->mZoomToFillButton = new StatusBarToolButton; d->mZoomToFitButton->setCheckable(true); d->mActualSizeButton->setCheckable(true); d->mZoomToFillButton->setCheckable(true); d->mZoomToFitButton->setChecked(true); if (QApplication::isLeftToRight()) { d->mZoomToFitButton->setGroupPosition(StatusBarToolButton::GroupLeft); d->mZoomToFillButton->setGroupPosition(StatusBarToolButton::GroupCenter); d->mActualSizeButton->setGroupPosition(StatusBarToolButton::GroupRight); } else { d->mActualSizeButton->setGroupPosition(StatusBarToolButton::GroupLeft); d->mZoomToFillButton->setGroupPosition(StatusBarToolButton::GroupCenter); d->mZoomToFitButton->setGroupPosition(StatusBarToolButton::GroupRight); } d->mZoomLabel = new QLabel; - d->mZoomLabel->setFixedWidth(d->mZoomLabel->fontMetrics().width(" 1000% ")); + d->mZoomLabel->setFixedWidth(d->mZoomLabel->fontMetrics().width(QStringLiteral(" 1000% "))); d->mZoomLabel->setAlignment(Qt::AlignCenter); d->mZoomSlider = new ZoomSlider; d->mZoomSlider->setMinimumWidth(150); d->mZoomSlider->slider()->setSingleStep(int(PRECISION)); d->mZoomSlider->slider()->setPageStep(3 * int(PRECISION)); connect(d->mZoomSlider->slider(), SIGNAL(actionTriggered(int)), SLOT(slotZoomSliderActionTriggered())); // Layout QHBoxLayout* layout = new QHBoxLayout(this); layout->setMargin(0); layout->setSpacing(0); layout->addWidget(d->mZoomToFitButton); layout->addWidget(d->mZoomToFillButton); layout->addWidget(d->mActualSizeButton); layout->addWidget(d->mZoomSlider); layout->addWidget(d->mZoomLabel); } ZoomWidget::~ZoomWidget() { delete d; } void ZoomWidget::setActions(QAction* zoomToFitAction, QAction* actualSizeAction, QAction* zoomInAction, QAction* zoomOutAction, QAction* zoomToFillAction) { d->mZoomToFitAction = zoomToFitAction; d->mActualSizeAction = actualSizeAction; d->mZoomToFillAction = zoomToFillAction; d->mZoomToFitButton->setDefaultAction(zoomToFitAction); d->mActualSizeButton->setDefaultAction(actualSizeAction); d->mZoomToFillButton->setDefaultAction(zoomToFillAction); d->mZoomSlider->setZoomInAction(zoomInAction); d->mZoomSlider->setZoomOutAction(zoomOutAction); QActionGroup *actionGroup = new QActionGroup(d->q); actionGroup->addAction(d->mZoomToFitAction); actionGroup->addAction(d->mZoomToFillAction); actionGroup->addAction(d->mActualSizeAction); actionGroup->setExclusive(true); // Adjust sizes int width = std::max({d->mZoomToFitButton->sizeHint().width(), d->mActualSizeButton->sizeHint().width(), d->mZoomToFillButton->sizeHint().width()}); d->mZoomToFitButton->setFixedWidth(width); d->mActualSizeButton->setFixedWidth(width); d->mZoomToFillButton->setFixedWidth(width); } void ZoomWidget::slotZoomSliderActionTriggered() { // The slider value changed because of the user (not because of range // changes). In this case disable zoom and apply slider value. d->emitZoomChanged(); } void ZoomWidget::setZoom(qreal zoom) { int intZoom = qRound(zoom * 100); - d->mZoomLabel->setText(QString("%1%").arg(intZoom)); + d->mZoomLabel->setText(QStringLiteral("%1%").arg(intZoom)); // Don't change slider value if we come here because the slider change, // avoids choppy sliding scroll. if (!d->mZoomUpdatedBySlider) { QSlider* slider = d->mZoomSlider->slider(); SignalBlocker blocker(slider); int value = sliderValueForZoom(zoom); if (value < slider->minimum()) { // It is possible that we are called *before* setMinimumZoom() as // been called. In this case, define the minimum ourself. d->mZoomSlider->setMinimum(value); } d->mZoomSlider->setValue(value); } } void ZoomWidget::setMinimumZoom(qreal minimumZoom) { d->mZoomSlider->setMinimum(sliderValueForZoom(minimumZoom)); } void ZoomWidget::setMaximumZoom(qreal zoom) { d->mZoomSlider->setMaximum(sliderValueForZoom(zoom)); } } // namespace diff --git a/part/gvpart.h b/part/gvpart.h index 28729d09..6a36f50b 100644 --- a/part/gvpart.h +++ b/part/gvpart.h @@ -1,61 +1,61 @@ /* 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. */ #ifndef GVPART_H #define GVPART_H // Qt // KDE #include // Local #include class KAboutData; namespace Gwenview { class DocumentView; class GVPart : public KParts::ReadOnlyPart { Q_OBJECT public: GVPart(QWidget* parentWidget, QObject* parent, const QVariantList&); static KAboutData* createAboutData(); protected: - bool openUrl(const QUrl &url) Q_DECL_OVERRIDE; + bool openUrl(const QUrl &url) override; private Q_SLOTS: void showContextMenu(); void showProperties(); void saveAs(); void showJobError(KJob*); private: DocumentView* mDocumentView; }; } // namespace #endif /* GVPART_H */ diff --git a/tests/auto/contextmanagertest.cpp b/tests/auto/contextmanagertest.cpp index 8e745621..c24bcac7 100644 --- a/tests/auto/contextmanagertest.cpp +++ b/tests/auto/contextmanagertest.cpp @@ -1,117 +1,117 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2013 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 "contextmanagertest.h" // Local #include #include #include // Qt #include #include // KDE #include #include #include using namespace Gwenview; using namespace TestUtils; QTEST_MAIN(ContextManagerTest) void ContextManagerTest::testRemove() { // When the current image is removed Gwenview must go to the next image if // there is any, otherwise to the previous image. SandBoxDir sandBox; sandBox.fill(QStringList() << "a" << "b" << "c"); QUrl dirUrl = QUrl::fromLocalFile(sandBox.absolutePath()); SortedDirModel dirModel; { QEventLoop loop; connect(dirModel.dirLister(), SIGNAL(completed()), &loop, SLOT(quit())); dirModel.dirLister()->openUrl(dirUrl); loop.exec(); } QCOMPARE(dirModel.rowCount(), 3); ContextManager manager(&dirModel, 0); // Select second row manager.selectionModel()->setCurrentIndex(dirModel.index(1, 0), QItemSelectionModel::Select); // Remove "b", `manager` should select "c" sandBox.remove("b"); dirModel.dirLister()->updateDirectory(dirUrl); while (dirModel.rowCount() == 3) { QTest::qWait(100); } QModelIndex currentIndex = manager.selectionModel()->currentIndex(); QCOMPARE(currentIndex.row(), 1); - QCOMPARE(currentIndex.data(Qt::DisplayRole).toString(), QString("c")); + QCOMPARE(currentIndex.data(Qt::DisplayRole).toString(), QStringLiteral("c")); // Remove "c", `manager` should select "a" sandBox.remove("c"); dirModel.dirLister()->updateDirectory(dirUrl); while (dirModel.rowCount() == 2) { QTest::qWait(100); } currentIndex = manager.selectionModel()->currentIndex(); QCOMPARE(currentIndex.row(), 0); - QCOMPARE(currentIndex.data(Qt::DisplayRole).toString(), QString("a")); + QCOMPARE(currentIndex.data(Qt::DisplayRole).toString(), QStringLiteral("a")); } void ContextManagerTest::testInvalidDirUrl() { class DirLister : public KDirLister { public: DirLister() : mOpenUrlCalled(false) { setAutoErrorHandlingEnabled(false, nullptr); } - bool openUrl(const QUrl &url, OpenUrlFlags flags = NoFlags) Q_DECL_OVERRIDE + bool openUrl(const QUrl &url, OpenUrlFlags flags = NoFlags) override { mOpenUrlCalled = true; return KDirLister::openUrl(url, flags); } bool mOpenUrlCalled; }; SortedDirModel dirModel; DirLister* dirLister = new DirLister; dirModel.setDirLister(dirLister); ContextManager manager(&dirModel, nullptr); manager.setCurrentDirUrl(QUrl()); QVERIFY(!dirLister->mOpenUrlCalled); } diff --git a/tests/auto/documenttest.cpp b/tests/auto/documenttest.cpp index bb327b0e..99ecea0d 100644 --- a/tests/auto/documenttest.cpp +++ b/tests/auto/documenttest.cpp @@ -1,894 +1,894 @@ /* 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. */ // Qt #include #include #include // KDE #include #include #include #include #include // Local #include "../lib/abstractimageoperation.h" #include "../lib/document/abstractdocumenteditor.h" #include "../lib/document/documentjob.h" #include "../lib/document/documentfactory.h" #include "../lib/imagemetainfomodel.h" #include "../lib/imageutils.h" #include "../lib/transformimageoperation.h" #include "testutils.h" #include #include "documenttest.h" QTEST_MAIN(DocumentTest) using namespace Gwenview; static void waitUntilMetaInfoLoaded(Document::Ptr doc) { while (doc->loadingState() < Document::MetaInfoLoaded) { QTest::qWait(100); } } static bool waitUntilJobIsDone(DocumentJob* job) { JobWatcher watcher(job); watcher.wait(); return watcher.error() == KJob::NoError; } void DocumentTest::initTestCase() { qRegisterMetaType("QUrl"); } void DocumentTest::init() { DocumentFactory::instance()->clearCache(); } void DocumentTest::testLoad() { QFETCH(QString, fileName); QFETCH(QByteArray, expectedFormat); QFETCH(int, expectedKindInt); QFETCH(bool, expectedIsAnimated); QFETCH(QImage, expectedImage); QFETCH(int, maxHeight); // number of lines to test. -1 to test all lines MimeTypeUtils::Kind expectedKind = MimeTypeUtils::Kind(expectedKindInt); QUrl url = urlForTestFile(fileName); // testing RAW loading. For raw, QImage directly won't work -> load it using KDCRaw QByteArray mFormatHint = url.fileName().section('.', -1).toLocal8Bit().toLower(); if (KDcrawIface::KDcraw::rawFilesList().contains(QString(mFormatHint))) { if (!KDcrawIface::KDcraw::loadEmbeddedPreview(expectedImage, url.toLocalFile())) { QSKIP("Not running this test: failed to get expectedImage. Try running ./fetch_testing_raw.sh\ in the tests/data directory and then rerun the tests."); } } if (expectedKind != MimeTypeUtils::KIND_SVG_IMAGE) { if (expectedImage.isNull()) { QSKIP("Not running this test: QImage failed to load the test image"); } } Document::Ptr doc = DocumentFactory::instance()->load(url); QSignalSpy spy(doc.data(), SIGNAL(isAnimatedUpdated())); doc->waitUntilLoaded(); QCOMPARE(doc->loadingState(), Document::Loaded); QCOMPARE(doc->kind(), expectedKind); QCOMPARE(doc->isAnimated(), expectedIsAnimated); QCOMPARE(spy.count(), doc->isAnimated() ? 1 : 0); if (doc->kind() == MimeTypeUtils::KIND_RASTER_IMAGE) { QImage image = doc->image(); if (maxHeight > -1) { QRect poiRect(0, 0, image.width(), maxHeight); image = image.copy(poiRect); expectedImage = expectedImage.copy(poiRect); } QCOMPARE(image, expectedImage); QCOMPARE(QString(doc->format()), QString(expectedFormat)); } } static void testLoad_newRow( const char* fileName, const QByteArray& format, MimeTypeUtils::Kind kind = MimeTypeUtils::KIND_RASTER_IMAGE, bool isAnimated = false, int maxHeight = -1 ) { QTest::newRow(fileName) << fileName << QByteArray(format) << int(kind) << isAnimated << QImage(pathForTestFile(fileName), format) << maxHeight; } void DocumentTest::testLoad_data() { QTest::addColumn("fileName"); QTest::addColumn("expectedFormat"); QTest::addColumn("expectedKindInt"); QTest::addColumn("expectedIsAnimated"); QTest::addColumn("expectedImage"); QTest::addColumn("maxHeight"); testLoad_newRow("test.png", "png"); testLoad_newRow("160216_no_size_before_decoding.eps", "eps"); testLoad_newRow("160382_corrupted.jpeg", "jpeg", MimeTypeUtils::KIND_RASTER_IMAGE, false, 55); testLoad_newRow("1x10k.png", "png"); testLoad_newRow("1x10k.jpg", "jpeg"); testLoad_newRow("test.xcf", "xcf"); testLoad_newRow("188191_does_not_load.tga", "tga"); testLoad_newRow("289819_does_not_load.png", "png"); testLoad_newRow("png-with-jpeg-extension.jpg", "png"); testLoad_newRow("jpg-with-gif-extension.gif", "jpeg"); // RAW preview testLoad_newRow("CANON-EOS350D-02.CR2", "cr2", MimeTypeUtils::KIND_RASTER_IMAGE, false); testLoad_newRow("dsc_0093.nef", "nef", MimeTypeUtils::KIND_RASTER_IMAGE, false); // SVG testLoad_newRow("test.svg", "", MimeTypeUtils::KIND_SVG_IMAGE); // FIXME: Test svgz // Animated testLoad_newRow("4frames.gif", "gif", MimeTypeUtils::KIND_RASTER_IMAGE, true); testLoad_newRow("1frame.gif", "gif", MimeTypeUtils::KIND_RASTER_IMAGE, false); testLoad_newRow("185523_1frame_with_graphic_control_extension.gif", "gif", MimeTypeUtils::KIND_RASTER_IMAGE, false); } void DocumentTest::testLoadTwoPasses() { QUrl url = urlForTestFile("test.png"); QImage image; bool ok = image.load(url.toLocalFile()); QVERIFY2(ok, "Could not load 'test.png'"); Document::Ptr doc = DocumentFactory::instance()->load(url); waitUntilMetaInfoLoaded(doc); QVERIFY2(doc->image().isNull(), "Image shouldn't have been loaded at this time"); QCOMPARE(doc->format().data(), "png"); doc->waitUntilLoaded(); QCOMPARE(image, doc->image()); } void DocumentTest::testLoadEmpty() { QUrl url = urlForTestFile("empty.png"); Document::Ptr doc = DocumentFactory::instance()->load(url); while (doc->loadingState() <= Document::KindDetermined) { QTest::qWait(100); } QCOMPARE(doc->loadingState(), Document::LoadingFailed); } #define NEW_ROW(fileName) QTest::newRow(fileName) << fileName void DocumentTest::testLoadDownSampled_data() { QTest::addColumn("fileName"); NEW_ROW("orient6.jpg"); NEW_ROW("1x10k.jpg"); } #undef NEW_ROW void DocumentTest::testLoadDownSampled() { // Note: for now we only support down sampling on jpeg, do not use test.png // here QFETCH(QString, fileName); QUrl url = urlForTestFile(fileName); QImage image; bool ok = image.load(url.toLocalFile()); QVERIFY2(ok, "Could not load test image"); Document::Ptr doc = DocumentFactory::instance()->load(url); QSignalSpy downSampledImageReadySpy(doc.data(), SIGNAL(downSampledImageReady())); QSignalSpy loadingFailedSpy(doc.data(), SIGNAL(loadingFailed(QUrl))); QSignalSpy loadedSpy(doc.data(), SIGNAL(loaded(QUrl))); bool ready = doc->prepareDownSampledImageForZoom(0.2); QVERIFY2(!ready, "There should not be a down sampled image at this point"); while (downSampledImageReadySpy.count() == 0 && loadingFailedSpy.count() == 0 && loadedSpy.count() == 0) { QTest::qWait(100); } QImage downSampledImage = doc->downSampledImageForZoom(0.2); QVERIFY2(!downSampledImage.isNull(), "Down sampled image should not be null"); QSize expectedSize = doc->size() / 2; if (expectedSize.isEmpty()) { expectedSize = image.size(); } QCOMPARE(downSampledImage.size(), expectedSize); } /** * Down sampling is not supported on png. We should get a complete image * instead. */ void DocumentTest::testLoadDownSampledPng() { QUrl url = urlForTestFile("test.png"); QImage image; bool ok = image.load(url.toLocalFile()); QVERIFY2(ok, "Could not load test image"); Document::Ptr doc = DocumentFactory::instance()->load(url); LoadingStateSpy stateSpy(doc); connect(doc.data(), SIGNAL(loaded(QUrl)), &stateSpy, SLOT(readState())); bool ready = doc->prepareDownSampledImageForZoom(0.2); QVERIFY2(!ready, "There should not be a down sampled image at this point"); doc->waitUntilLoaded(); QCOMPARE(stateSpy.mCallCount, 1); QCOMPARE(stateSpy.mState, Document::Loaded); } void DocumentTest::testLoadRemote() { QUrl url = setUpRemoteTestDir("test.png"); if (!url.isValid()) { QSKIP("Not running this test: failed to setup remote test dir."); } url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + '/' + "test.png"); QVERIFY2(KIO::stat(url, KIO::StatJob::SourceSide, 0)->exec(), "test url not found"); Document::Ptr doc = DocumentFactory::instance()->load(url); doc->waitUntilLoaded(); QImage image = doc->image(); QCOMPARE(image.width(), 150); QCOMPARE(image.height(), 100); } void DocumentTest::testLoadAnimated() { QUrl srcUrl = urlForTestFile("40frames.gif"); Document::Ptr doc = DocumentFactory::instance()->load(srcUrl); QSignalSpy spy(doc.data(), SIGNAL(imageRectUpdated(QRect))); doc->waitUntilLoaded(); QVERIFY(doc->isAnimated()); // Test we receive only one imageRectUpdated() until animation is started // (the imageRectUpdated() is triggered by the loading of the first image) QTest::qWait(1000); QCOMPARE(spy.count(), 1); // Test we now receive some imageRectUpdated() doc->startAnimation(); QTest::qWait(1000); int count = spy.count(); doc->stopAnimation(); QVERIFY2(count > 0, "No imageRectUpdated() signal received"); // Test we do not receive imageRectUpdated() anymore QTest::qWait(1000); QCOMPARE(count, spy.count()); // Start again, we should receive imageRectUpdated() again doc->startAnimation(); QTest::qWait(1000); QVERIFY2(spy.count() > count, "No imageRectUpdated() signal received after restarting"); } void DocumentTest::testPrepareDownSampledAfterFailure() { QUrl url = urlForTestFile("empty.png"); Document::Ptr doc = DocumentFactory::instance()->load(url); doc->waitUntilLoaded(); QCOMPARE(doc->loadingState(), Document::LoadingFailed); bool ready = doc->prepareDownSampledImageForZoom(0.25); QVERIFY2(!ready, "Down sampled image should not be ready"); } void DocumentTest::testSaveRemote() { QUrl dstUrl = setUpRemoteTestDir(); if (!dstUrl.isValid()) { QSKIP("Not running this test: failed to setup remote test dir."); } QUrl srcUrl = urlForTestFile("test.png"); Document::Ptr doc = DocumentFactory::instance()->load(srcUrl); doc->waitUntilLoaded(); dstUrl = dstUrl.adjusted(QUrl::StripTrailingSlash); dstUrl.setPath(dstUrl.path() + '/' + "testSaveRemote.png"); QVERIFY(waitUntilJobIsDone(doc->save(dstUrl, "png"))); } /** * Check that deleting a document while it is loading does not crash */ void DocumentTest::testDeleteWhileLoading() { { QUrl url = urlForTestFile("test.png"); QImage image; bool ok = image.load(url.toLocalFile()); QVERIFY2(ok, "Could not load 'test.png'"); Document::Ptr doc = DocumentFactory::instance()->load(url); } DocumentFactory::instance()->clearCache(); // Wait two seconds. If the test fails we will get a segfault while waiting QTest::qWait(2000); } void DocumentTest::testLoadRotated() { QUrl url = urlForTestFile("orient6.jpg"); QImage image; bool ok = image.load(url.toLocalFile()); QVERIFY2(ok, "Could not load 'orient6.jpg'"); QMatrix matrix = ImageUtils::transformMatrix(ROT_90); image = image.transformed(matrix); Document::Ptr doc = DocumentFactory::instance()->load(url); doc->waitUntilLoaded(); QCOMPARE(image, doc->image()); // RAW preview on rotated image url = urlForTestFile("dsd_1838.nef"); if (!KDcrawIface::KDcraw::loadEmbeddedPreview(image, url.toLocalFile())) { QSKIP("Not running this test: failed to get image. Try running ./fetch_testing_raw.sh\ in the tests/data directory and then rerun the tests."); } matrix = ImageUtils::transformMatrix(ROT_270); image = image.transformed(matrix); doc = DocumentFactory::instance()->load(url); doc->waitUntilLoaded(); QCOMPARE(image, doc->image()); } /** * Checks that asking the DocumentFactory the same document twice in a row does * not load it twice */ void DocumentTest::testMultipleLoads() { QUrl url = urlForTestFile("orient6.jpg"); Document::Ptr doc1 = DocumentFactory::instance()->load(url); Document::Ptr doc2 = DocumentFactory::instance()->load(url); QCOMPARE(doc1.data(), doc2.data()); } void DocumentTest::testSaveAs() { QUrl url = urlForTestFile("orient6.jpg"); DocumentFactory* factory = DocumentFactory::instance(); Document::Ptr doc = factory->load(url); QSignalSpy savedSpy(doc.data(), SIGNAL(saved(QUrl,QUrl))); QSignalSpy modifiedDocumentListChangedSpy(factory, SIGNAL(modifiedDocumentListChanged())); QSignalSpy documentChangedSpy(factory, SIGNAL(documentChanged(QUrl))); doc->startLoadingFullImage(); QUrl destUrl = urlForTestOutputFile("result.png"); QVERIFY(waitUntilJobIsDone(doc->save(destUrl, "png"))); QCOMPARE(doc->format().data(), "png"); QCOMPARE(doc->url(), destUrl); QCOMPARE(doc->metaInfo()->getValueForKey("General.Name"), destUrl.fileName()); QVERIFY2(doc->loadingState() == Document::Loaded, "Document is supposed to finish loading before saving" ); QTest::qWait(100); // saved() is emitted asynchronously QCOMPARE(savedSpy.count(), 1); QVariantList args = savedSpy.takeFirst(); QCOMPARE(args.at(0).value(), url); QCOMPARE(args.at(1).value(), destUrl); QImage image("result.png", "png"); QCOMPARE(doc->image(), image); QVERIFY(!DocumentFactory::instance()->hasUrl(url)); QVERIFY(DocumentFactory::instance()->hasUrl(destUrl)); QCOMPARE(modifiedDocumentListChangedSpy.count(), 0); // No changes were made QCOMPARE(documentChangedSpy.count(), 1); args = documentChangedSpy.takeFirst(); QCOMPARE(args.at(0).value(), destUrl); } void DocumentTest::testLosslessSave() { QUrl url1 = urlForTestFile("orient6.jpg"); Document::Ptr doc = DocumentFactory::instance()->load(url1); doc->startLoadingFullImage(); QUrl url2 = urlForTestOutputFile("orient1.jpg"); QVERIFY(waitUntilJobIsDone(doc->save(url2, "jpeg"))); QImage image1; QVERIFY(image1.load(url1.toLocalFile())); QImage image2; QVERIFY(image2.load(url2.toLocalFile())); QCOMPARE(image1, image2); } void DocumentTest::testLosslessRotate() { // Generate test image QImage image1(200, 96, QImage::Format_RGB32); { QPainter painter(&image1); QConicalGradient gradient(QPointF(100, 48), 100); gradient.setColorAt(0, Qt::white); gradient.setColorAt(1, Qt::blue); painter.fillRect(image1.rect(), gradient); } QUrl url1 = urlForTestOutputFile("lossless1.jpg"); QVERIFY(image1.save(url1.toLocalFile(), "jpeg")); // Load it as a Gwenview document Document::Ptr doc = DocumentFactory::instance()->load(url1); doc->waitUntilLoaded(); // Rotate one time QVERIFY(doc->editor()); doc->editor()->applyTransformation(ROT_90); // Save it QUrl url2 = urlForTestOutputFile("lossless2.jpg"); waitUntilJobIsDone(doc->save(url2, "jpeg")); // Load the saved image doc = DocumentFactory::instance()->load(url2); doc->waitUntilLoaded(); // Rotate the other way QVERIFY(doc->editor()); doc->editor()->applyTransformation(ROT_270); waitUntilJobIsDone(doc->save(url2, "jpeg")); // Compare the saved images QVERIFY(image1.load(url1.toLocalFile())); QImage image2; QVERIFY(image2.load(url2.toLocalFile())); QCOMPARE(image1, image2); } void DocumentTest::testModifyAndSaveAs() { QVariantList args; class TestOperation : public AbstractImageOperation { public: void redo() { QImage image(10, 10, QImage::Format_ARGB32); image.fill(QColor(Qt::white).rgb()); document()->editor()->setImage(image); finish(true); } }; QUrl url = urlForTestFile("orient6.jpg"); DocumentFactory* factory = DocumentFactory::instance(); Document::Ptr doc = factory->load(url); QSignalSpy savedSpy(doc.data(), SIGNAL(saved(QUrl,QUrl))); QSignalSpy modifiedDocumentListChangedSpy(factory, SIGNAL(modifiedDocumentListChanged())); QSignalSpy documentChangedSpy(factory, SIGNAL(documentChanged(QUrl))); doc->waitUntilLoaded(); QVERIFY(!doc->isModified()); QCOMPARE(modifiedDocumentListChangedSpy.count(), 0); // Modify image QVERIFY(doc->editor()); TestOperation* op = new TestOperation; op->applyToDocument(doc); QTest::qWait(100); QVERIFY(doc->isModified()); QCOMPARE(modifiedDocumentListChangedSpy.count(), 1); modifiedDocumentListChangedSpy.clear(); QList lst = factory->modifiedDocumentList(); QCOMPARE(lst.count(), 1); QCOMPARE(lst.first(), url); QCOMPARE(documentChangedSpy.count(), 1); args = documentChangedSpy.takeFirst(); QCOMPARE(args.at(0).value(), url); // Save it under a new name QUrl destUrl = urlForTestOutputFile("modify.png"); QVERIFY(waitUntilJobIsDone(doc->save(destUrl, "png"))); // Wait a bit because save() will clear the undo stack when back to the // event loop QTest::qWait(100); QVERIFY(!doc->isModified()); QVERIFY(!factory->hasUrl(url)); QVERIFY(factory->hasUrl(destUrl)); QCOMPARE(modifiedDocumentListChangedSpy.count(), 1); QVERIFY(DocumentFactory::instance()->modifiedDocumentList().isEmpty()); QCOMPARE(documentChangedSpy.count(), 2); QList modifiedUrls = QList() << url << destUrl; QVERIFY(modifiedUrls.contains(url)); QVERIFY(modifiedUrls.contains(destUrl)); } void DocumentTest::testMetaInfoJpeg() { QUrl url = urlForTestFile("orient6.jpg"); Document::Ptr doc = DocumentFactory::instance()->load(url); // We cleared the cache, so the document should not be loaded Q_ASSERT(doc->loadingState() <= Document::KindDetermined); // Wait until we receive the metaInfoUpdated() signal QSignalSpy metaInfoUpdatedSpy(doc.data(), SIGNAL(metaInfoUpdated())); while (metaInfoUpdatedSpy.count() == 0) { QTest::qWait(100); } // Extract an exif key QString value = doc->metaInfo()->getValueForKey("Exif.Image.Make"); QCOMPARE(value, QString::fromUtf8("Canon")); } void DocumentTest::testMetaInfoBmp() { QUrl url = urlForTestOutputFile("metadata.bmp"); const int width = 200; const int height = 100; QImage image(width, height, QImage::Format_ARGB32); image.fill(Qt::black); image.save(url.toLocalFile(), "BMP"); Document::Ptr doc = DocumentFactory::instance()->load(url); QSignalSpy metaInfoUpdatedSpy(doc.data(), SIGNAL(metaInfoUpdated())); waitUntilMetaInfoLoaded(doc); Q_ASSERT(metaInfoUpdatedSpy.count() >= 1); QString value = doc->metaInfo()->getValueForKey("General.ImageSize"); - QString expectedValue = QString("%1x%2").arg(width).arg(height); + QString expectedValue = QStringLiteral("%1x%2").arg(width).arg(height); QCOMPARE(value, expectedValue); } void DocumentTest::testForgetModifiedDocument() { QSignalSpy spy(DocumentFactory::instance(), SIGNAL(modifiedDocumentListChanged())); DocumentFactory::instance()->forget(QUrl("file://does/not/exist.png")); QCOMPARE(spy.count(), 0); // Generate test image QImage image1(200, 96, QImage::Format_RGB32); { QPainter painter(&image1); QConicalGradient gradient(QPointF(100, 48), 100); gradient.setColorAt(0, Qt::white); gradient.setColorAt(1, Qt::blue); painter.fillRect(image1.rect(), gradient); } QUrl url = urlForTestOutputFile("testForgetModifiedDocument.png"); QVERIFY(image1.save(url.toLocalFile(), "png")); // Load it as a Gwenview document Document::Ptr doc = DocumentFactory::instance()->load(url); doc->waitUntilLoaded(); // Modify it TransformImageOperation* op = new TransformImageOperation(ROT_90); op->applyToDocument(doc); QTest::qWait(100); QCOMPARE(spy.count(), 1); QList lst = DocumentFactory::instance()->modifiedDocumentList(); QCOMPARE(lst.length(), 1); QCOMPARE(lst.first(), url); // Forget it DocumentFactory::instance()->forget(url); QCOMPARE(spy.count(), 2); lst = DocumentFactory::instance()->modifiedDocumentList(); QVERIFY(lst.isEmpty()); } void DocumentTest::testModifiedAndSavedSignals() { TransformImageOperation* op; QUrl url = urlForTestFile("orient6.jpg"); Document::Ptr doc = DocumentFactory::instance()->load(url); QSignalSpy modifiedSpy(doc.data(), SIGNAL(modified(QUrl))); QSignalSpy savedSpy(doc.data(), SIGNAL(saved(QUrl,QUrl))); doc->waitUntilLoaded(); QCOMPARE(modifiedSpy.count(), 0); QCOMPARE(savedSpy.count(), 0); op = new TransformImageOperation(ROT_90); op->applyToDocument(doc); QTest::qWait(100); QCOMPARE(modifiedSpy.count(), 1); op = new TransformImageOperation(ROT_90); op->applyToDocument(doc); QTest::qWait(100); QCOMPARE(modifiedSpy.count(), 2); doc->undoStack()->undo(); QTest::qWait(100); QCOMPARE(modifiedSpy.count(), 3); doc->undoStack()->undo(); QTest::qWait(100); QCOMPARE(savedSpy.count(), 1); } class TestJob : public DocumentJob { public: TestJob(QString* str, char ch) : mStr(str) , mCh(ch) {} protected: virtual void doStart() { *mStr += mCh; emitResult(); } private: QString* mStr; char mCh; }; void DocumentTest::testJobQueue() { QUrl url = urlForTestFile("orient6.jpg"); Document::Ptr doc = DocumentFactory::instance()->load(url); QSignalSpy spy(doc.data(), SIGNAL(busyChanged(QUrl,bool))); QString str; doc->enqueueJob(new TestJob(&str, 'a')); doc->enqueueJob(new TestJob(&str, 'b')); doc->enqueueJob(new TestJob(&str, 'c')); QVERIFY(doc->isBusy()); QEventLoop loop; connect(doc.data(), SIGNAL(allTasksDone()), &loop, SLOT(quit())); loop.exec(); QVERIFY(!doc->isBusy()); QCOMPARE(spy.count(), 2); QVariantList row = spy.takeFirst(); QCOMPARE(row.at(0).value(), url); QVERIFY(row.at(1).toBool()); row = spy.takeFirst(); QCOMPARE(row.at(0).value(), url); QVERIFY(!row.at(1).toBool()); - QCOMPARE(str, QString("abc")); + QCOMPARE(str, QStringLiteral("abc")); } class TestCheckDocumentEditorJob : public DocumentJob { public: TestCheckDocumentEditorJob(int* hasEditor) : mHasEditor(hasEditor) { *mHasEditor = -1; } protected: virtual void doStart() { document()->waitUntilLoaded(); *mHasEditor = checkDocumentEditor() ? 1 : 0; emitResult(); } private: int* mHasEditor; }; class TestUiDelegate : public KJobUiDelegate { public: TestUiDelegate(bool* showErrorMessageCalled) : mShowErrorMessageCalled(showErrorMessageCalled) { setAutoErrorHandlingEnabled(true); *mShowErrorMessageCalled = false; } virtual void showErrorMessage() { //qDebug(); *mShowErrorMessageCalled = true; } private: bool* mShowErrorMessageCalled; }; /** * Test that an error is reported when a DocumentJob fails because there is no * document editor available */ void DocumentTest::testCheckDocumentEditor() { int hasEditor; bool showErrorMessageCalled; QEventLoop loop; Document::Ptr doc; TestCheckDocumentEditorJob* job; doc = DocumentFactory::instance()->load(urlForTestFile("orient6.jpg")); job = new TestCheckDocumentEditorJob(&hasEditor); job->setUiDelegate(new TestUiDelegate(&showErrorMessageCalled)); doc->enqueueJob(job); connect(doc.data(), SIGNAL(allTasksDone()), &loop, SLOT(quit())); loop.exec(); QVERIFY(!showErrorMessageCalled); QCOMPARE(hasEditor, 1); doc = DocumentFactory::instance()->load(urlForTestFile("test.svg")); job = new TestCheckDocumentEditorJob(&hasEditor); job->setUiDelegate(new TestUiDelegate(&showErrorMessageCalled)); doc->enqueueJob(job); connect(doc.data(), SIGNAL(allTasksDone()), &loop, SLOT(quit())); loop.exec(); QVERIFY(showErrorMessageCalled); QCOMPARE(hasEditor, 0); } /** * An operation should only pushed to the document undo stack if it succeed */ void DocumentTest::testUndoStackPush() { class SuccessOperation : public AbstractImageOperation { protected: virtual void redo() { QMetaObject::invokeMethod(this, "finish", Qt::QueuedConnection, Q_ARG(bool, true)); } }; class FailureOperation : public AbstractImageOperation { protected: virtual void redo() { QMetaObject::invokeMethod(this, "finish", Qt::QueuedConnection, Q_ARG(bool, false)); } }; AbstractImageOperation* op; Document::Ptr doc = DocumentFactory::instance()->load(urlForTestFile("orient6.jpg")); // A successful operation should be added to the undo stack op = new SuccessOperation; op->applyToDocument(doc); QTest::qWait(100); QVERIFY(!doc->undoStack()->isClean()); // Reset doc->undoStack()->undo(); QVERIFY(doc->undoStack()->isClean()); // A failed operation should not be added to the undo stack op = new FailureOperation; op->applyToDocument(doc); QTest::qWait(100); QVERIFY(doc->undoStack()->isClean()); } void DocumentTest::testUndoRedo() { class SuccessOperation : public AbstractImageOperation { public: int mRedoCount = 0; int mUndoCount = 0; protected: virtual void redo() { mRedoCount++; finish(true); } virtual void undo() { mUndoCount++; finish(true); } }; Document::Ptr doc = DocumentFactory::instance()->load(urlForTestFile("orient6.jpg")); QSignalSpy modifiedSpy(doc.data(), &Document::modified); QSignalSpy savedSpy(doc.data(), &Document::saved); SuccessOperation* op = new SuccessOperation; QCOMPARE(op->mRedoCount, 0); QCOMPARE(op->mUndoCount, 0); // Apply (redo) operation op->applyToDocument(doc); QVERIFY(modifiedSpy.wait()); QCOMPARE(op->mRedoCount, 1); QCOMPARE(op->mUndoCount, 0); QCOMPARE(doc->undoStack()->count(), 1); QVERIFY(!doc->undoStack()->isClean()); // Undo operation doc->undoStack()->undo(); QVERIFY(savedSpy.wait()); QCOMPARE(op->mRedoCount, 1); QCOMPARE(op->mUndoCount, 1); QCOMPARE(doc->undoStack()->count(), 1); QVERIFY(doc->undoStack()->isClean()); // Redo operation doc->undoStack()->redo(); QVERIFY(modifiedSpy.wait()); QCOMPARE(op->mRedoCount, 2); QCOMPARE(op->mUndoCount, 1); QCOMPARE(doc->undoStack()->count(), 1); QVERIFY(!doc->undoStack()->isClean()); // Undo operation again doc->undoStack()->undo(); QVERIFY(savedSpy.wait()); QCOMPARE(op->mRedoCount, 2); QCOMPARE(op->mUndoCount, 2); QCOMPARE(doc->undoStack()->count(), 1); QVERIFY(doc->undoStack()->isClean()); } diff --git a/tests/auto/jpegcontenttest.cpp b/tests/auto/jpegcontenttest.cpp index a1b6dbb5..aa2ebbf9 100644 --- a/tests/auto/jpegcontenttest.cpp +++ b/tests/auto/jpegcontenttest.cpp @@ -1,304 +1,304 @@ /* 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. */ #include "jpegcontenttest.h" #include // Qt #include #include #include #include // KDE #include #include // Local #include "../lib/orientation.h" #include "../lib/jpegcontent.h" #include "testutils.h" using namespace std; const char* ORIENT6_FILE = "orient6.jpg"; const char* ORIENT1_VFLIP_FILE = "orient1_vflip.jpg"; const char* CUT_FILE = "cut.jpg"; const char* TMP_FILE = "tmp.jpg"; const char* THUMBNAIL_FILE = "test_thumbnail.jpg"; const int ORIENT6_WIDTH = 128; // This size is the size *after* orientation const int ORIENT6_HEIGHT = 256; // has been applied const QString ORIENT6_COMMENT = "a comment"; QTEST_MAIN(JpegContentTest) void JpegContentTest::initTestCase() { bool result; QFile in(pathForTestFile(ORIENT6_FILE)); result = in.open(QIODevice::ReadOnly); QVERIFY(result); QFileInfo info(in); int size = info.size() / 2; char* data = new char[size]; int readSize = in.read(data, size); QCOMPARE(size, readSize); QFile out(CUT_FILE); result = out.open(QIODevice::WriteOnly); QVERIFY(result); int wroteSize = out.write(data, size); QCOMPARE(size, wroteSize); delete []data; } void JpegContentTest::cleanupTestCase() { QDir::current().remove(CUT_FILE); } typedef QMap MetaInfoMap; #if 0 MetaInfoMap getMetaInfo(const QString& path) { KFileMetaInfo fmi(path); QStringList list = fmi.supportedKeys(); QStringList::ConstIterator it = list.constBegin(); MetaInfoMap map; for (; it != list.constEnd(); ++it) { KFileMetaInfoItem item = fmi.item(*it); map[*it] = item.value().toString(); } return map; } void compareMetaInfo(const QString& path1, const QString& path2, const QStringList& ignoredKeys) { MetaInfoMap mim1 = getMetaInfo(path1); MetaInfoMap mim2 = getMetaInfo(path2); QCOMPARE(mim1.keys(), mim2.keys()); QList keys = mim1.keys(); QList::ConstIterator it = keys.constBegin(); for (; it != keys.constEnd(); ++it) { QString key = *it; if (ignoredKeys.contains(key)) continue; QString msg = - QString("Meta info differs for key '%1': v1=%2 v2=%3") + QStringLiteral("Meta info differs for key '%1': v1=%2 v2=%3") .arg(key) .arg(mim1[key]) .arg(mim2[key]); QVERIFY2(mim1[key] == mim2[key], msg.toUtf8()); } } #endif void JpegContentTest::testResetOrientation() { Gwenview::JpegContent content; bool result; // Test resetOrientation without transform result = content.load(pathForTestFile(ORIENT6_FILE)); QVERIFY(result); content.resetOrientation(); result = content.save(TMP_FILE); QVERIFY(result); result = content.load(TMP_FILE); QVERIFY(result); QCOMPARE(content.orientation(), Gwenview::NORMAL); // Test resetOrientation with transform result = content.load(pathForTestFile(ORIENT6_FILE)); QVERIFY(result); content.resetOrientation(); content.transform(Gwenview::ROT_90); result = content.save(TMP_FILE); QVERIFY(result); result = content.load(TMP_FILE); QVERIFY(result); QCOMPARE(content.orientation(), Gwenview::NORMAL); } /** * This function tests JpegContent::transform() by applying a ROT_90 * transformation, saving, reloading and applying a ROT_270 to undo the ROT_90. * Saving and reloading are necessary because lossless transformation only * happens in JpegContent::save() */ void JpegContentTest::testTransform() { bool result; QImage finalImage, expectedImage; Gwenview::JpegContent content; result = content.load(pathForTestFile(ORIENT6_FILE)); QVERIFY(result); content.transform(Gwenview::ROT_90); result = content.save(TMP_FILE); QVERIFY(result); result = content.load(TMP_FILE); QVERIFY(result); content.transform(Gwenview::ROT_270); result = content.save(TMP_FILE); QVERIFY(result); result = finalImage.load(TMP_FILE); QVERIFY(result); result = expectedImage.load(pathForTestFile(ORIENT6_FILE)); QVERIFY(result); QCOMPARE(finalImage , expectedImage); } void JpegContentTest::testSetComment() { QString comment = "test comment"; Gwenview::JpegContent content; bool result; result = content.load(pathForTestFile(ORIENT6_FILE)); QVERIFY(result); content.setComment(comment); QCOMPARE(content.comment() , comment); result = content.save(TMP_FILE); QVERIFY(result); result = content.load(TMP_FILE); QVERIFY(result); QCOMPARE(content.comment() , comment); } void JpegContentTest::testReadInfo() { Gwenview::JpegContent content; bool result = content.load(pathForTestFile(ORIENT6_FILE)); QVERIFY(result); QCOMPARE(int(content.orientation()), 6); QCOMPARE(content.comment() , ORIENT6_COMMENT); QCOMPARE(content.size() , QSize(ORIENT6_WIDTH, ORIENT6_HEIGHT)); } void JpegContentTest::testThumbnail() { Gwenview::JpegContent content; bool result = content.load(pathForTestFile(ORIENT6_FILE)); QVERIFY(result); QImage thumbnail = content.thumbnail(); result = thumbnail.save(THUMBNAIL_FILE, "JPEG"); QVERIFY(result); } void JpegContentTest::testMultipleRotations() { // Test that rotating a file a lot of times does not cause findJxform() to fail Gwenview::JpegContent content; bool result = content.load(pathForTestFile(ORIENT6_FILE)); QVERIFY(result); result = content.load(pathForTestFile(ORIENT6_FILE)); QVERIFY(result); // 12*4 + 1 is the same as 1, since rotating four times brings you back for (int loop = 0; loop < 12 * 4 + 1; ++loop) { content.transform(Gwenview::ROT_90); } result = content.save(TMP_FILE); QVERIFY(result); result = content.load(TMP_FILE); QVERIFY(result); QCOMPARE(content.size() , QSize(ORIENT6_HEIGHT, ORIENT6_WIDTH)); // Check the other meta info are still here // QStringList ignoredKeys; // ignoredKeys << "Orientation" << "Comment"; // compareMetaInfo(pathForTestFile(ORIENT6_FILE), pathForTestFile(ORIENT1_VFLIP_FILE), ignoredKeys); } void JpegContentTest::testLoadTruncated() { // Test that loading and manipulating a truncated file does not crash Gwenview::JpegContent content; bool result = content.load(CUT_FILE); QVERIFY(result); QCOMPARE(int(content.orientation()), 6); QCOMPARE(content.comment() , ORIENT6_COMMENT); content.transform(Gwenview::VFLIP); qWarning() << "# Next function should output errors about incomplete image" ; content.save(TMP_FILE); qWarning() << "#" ; } void JpegContentTest::testRawData() { Gwenview::JpegContent content; bool result = content.load(pathForTestFile(ORIENT6_FILE)); QVERIFY(result); QByteArray fileData; QFile file(pathForTestFile(ORIENT6_FILE)); result = file.open(QIODevice::ReadOnly); QVERIFY(result); fileData = file.readAll(); QCOMPARE(content.rawData(), fileData); } void JpegContentTest::testSetImage() { Gwenview::JpegContent content; bool result = content.load(pathForTestFile(ORIENT6_FILE)); QVERIFY(result); QImage image = QImage(400, 300, QImage::Format_RGB32); image.fill(Qt::red); content.setImage(image); result = content.save(TMP_FILE); QVERIFY(result); result = content.load(TMP_FILE); QVERIFY(result); QCOMPARE(content.size(), image.size()); // QStringList ignoredKeys; // ignoredKeys << "Orientation"; // compareMetaInfo(pathForTestFile(ORIENT6_FILE), pathForTestFile(TMP_FILE), ignoredKeys); } diff --git a/tests/auto/testutils.h b/tests/auto/testutils.h index 1719df89..4e19a2b4 100644 --- a/tests/auto/testutils.h +++ b/tests/auto/testutils.h @@ -1,128 +1,128 @@ /* Gwenview: an image viewer Copyright 2008 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. */ #ifndef TESTUTILS_H #define TESTUTILS_H #include // Qt #include #include #include #include // KDE #include #include #include #include "config-gwenview.h" /* * This file contains simple helpers to access test files */ inline QString pathForTestFile(const QString& name) { - return QDir::cleanPath(QString("%1/%2").arg(GV_TEST_DATA_DIR).arg(name)); + return QDir::cleanPath(QStringLiteral("%1/%2").arg(GV_TEST_DATA_DIR).arg(name)); } inline QUrl urlForTestFile(const QString& name) { return QUrl::fromLocalFile(pathForTestFile(name)); } inline QString pathForTestOutputFile(const QString& name) { - return QString("%1/%2").arg(QDir::currentPath()).arg(name); + return QStringLiteral("%1/%2").arg(QDir::currentPath()).arg(name); } inline QUrl urlForTestOutputFile(const QString& name) { return QUrl::fromLocalFile(pathForTestOutputFile(name)); } inline bool waitForSignal(const QSignalSpy& spy, int timeout = 5) { for (int x = 0; x < timeout; ++x) { if (spy.count() > 0) { return true; } QTest::qWait(1000); } return false; } void createEmptyFile(const QString& path); /** * Returns the url of the remote url dir if remote test dir was successfully * set up. * If testFile is valid, it is copied into the test dir. */ QUrl setUpRemoteTestDir(const QString& testFile = QString()); /** * Make sure all objects on which deleteLater() have been called have been * destroyed. */ void waitForDeferredDeletes(); // FIXME: Top-level functions should move to the TestUtils namespace namespace TestUtils { bool fuzzyImageCompare(const QImage& img1, const QImage& img2, int delta = 2); bool imageCompare(const QImage& img1, const QImage& img2); void purgeUserConfiguration(); class SandBoxDir : public QDir { public: SandBoxDir(); void fill(const QStringList& files); private: QTemporaryDir mTempDir; }; /** * An event loop which stops itself after a predefined duration */ class TimedEventLoop : public QEventLoop { Q_OBJECT public: TimedEventLoop(int maxDurationInSeconds = 60); int exec(ProcessEventsFlags flags = AllEvents); private Q_SLOTS: void fail(); private: QTimer *mTimer; }; } // namespace #endif /* TESTUTILS_H */ diff --git a/tests/manual/imageloadbench.cpp b/tests/manual/imageloadbench.cpp index a7759917..8bbcd508 100644 --- a/tests/manual/imageloadbench.cpp +++ b/tests/manual/imageloadbench.cpp @@ -1,61 +1,61 @@ #include #include #include #include #include #include #include #include const int ITERATIONS = 2; const QSize SCALED_SIZE(1280, 800); static void bench(QIODevice* device, const QString& outputName) { QTime chrono; chrono.start(); for (int iteration = 0; iteration < ITERATIONS; ++iteration) { qDebug() << "Iteration:" << iteration; device->open(QIODevice::ReadOnly); QImageReader reader(device); QSize size = reader.size(); size.scale(SCALED_SIZE, Qt::KeepAspectRatio); reader.setScaledSize(size); QImage img = reader.read(); device->close(); if (iteration == ITERATIONS - 1) { qDebug() << "time:" << chrono.elapsed(); img.save(outputName, "png"); } } } int main(int argc, char** argv) { QCoreApplication app(argc, argv); if (argc != 2) { qDebug() << "Usage: imageloadbench "; return 1; } QString fileName = QString::fromUtf8(argv[1]); QFile file(fileName); if (!file.open(QIODevice::ReadOnly)) { - qDebug() << QString("Could not open '%1'").arg(fileName); + qDebug() << QStringLiteral("Could not open '%1'").arg(fileName); return 2; } QByteArray data = file.readAll(); QBuffer buffer(&data); qDebug() << "Using Qt loader"; bench(&buffer, "qt.png"); Gwenview::ImageFormats::registerPlugins(); qDebug() << "Using Gwenview loader"; bench(&buffer, "gv.png"); return 0; }