diff --git a/app/filtercontroller.cpp b/app/filtercontroller.cpp index 93cfb6d8..361b4f1e 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: - virtual void paintEvent(QPaintEvent*) + void paintEvent(QPaintEvent*) Q_DECL_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 44697f32..78ff82c2 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) {} - virtual bool needsSemanticInfo() const + bool needsSemanticInfo() const Q_DECL_OVERRIDE { return false; } - virtual bool acceptsIndex(const QModelIndex& index) const + bool acceptsIndex(const QModelIndex& index) const Q_DECL_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) {} - virtual bool needsSemanticInfo() const + bool needsSemanticInfo() const Q_DECL_OVERRIDE { return false; } - virtual bool acceptsIndex(const QModelIndex& index) const + bool acceptsIndex(const QModelIndex& index) const Q_DECL_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) {} - virtual bool needsSemanticInfo() const + bool needsSemanticInfo() const Q_DECL_OVERRIDE { return true; } - virtual bool acceptsIndex(const QModelIndex& index) const + bool acceptsIndex(const QModelIndex& index) const Q_DECL_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) {} - virtual bool needsSemanticInfo() const + bool needsSemanticInfo() const Q_DECL_OVERRIDE { return true; } - virtual bool acceptsIndex(const QModelIndex& index) const + bool acceptsIndex(const QModelIndex& index) const Q_DECL_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 5593c180..1c7150a4 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: UrlDropTreeView(QWidget* parent = 0) : QTreeView(parent) {} protected: - void dragEnterEvent(QDragEnterEvent* event) + void dragEnterEvent(QDragEnterEvent* event) Q_DECL_OVERRIDE { QAbstractItemView::dragEnterEvent(event); setDirtyRegion(mDropRect); if (event->mimeData()->hasUrls()) { event->acceptProposedAction(); } } - void dragMoveEvent(QDragMoveEvent* event) + void dragMoveEvent(QDragMoveEvent* event) Q_DECL_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) + void dropEvent(QDropEvent* event) Q_DECL_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 = 0; 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/imagemetainfodialog.cpp b/app/imagemetainfodialog.cpp index 660eba25..c74eedc4 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: - virtual void paint(QPainter* painter, const QStyleOptionViewItem& _option, const QModelIndex& index) const + void paint(QPainter* painter, const QStyleOptionViewItem& _option, const QModelIndex& index) const Q_DECL_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()); } } - virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const + QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const Q_DECL_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: ExpandedTreeView(QWidget* parent) : QTreeView(parent) {} protected: - virtual void rowsInserted(const QModelIndex& parent, int start, int end) + void rowsInserted(const QModelIndex& parent, int start, int end) Q_DECL_OVERRIDE { QTreeView::rowsInserted(parent, start, end); if (!parent.isValid()) { for (int row = start; row <= end; ++row) { setUpRootIndex(row); } } } - virtual void reset() + void reset() Q_DECL_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(0); } 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/infocontextmanageritem.cpp b/app/infocontextmanageritem.cpp index 62102e3b..51b57f3a 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: KeyValueWidget(QWidget* parent = 0) : QWidget(parent) { QSizePolicy policy(QSizePolicy::Preferred, QSizePolicy::Fixed); policy.setHeightForWidth(true); setSizePolicy(policy); } - QSize sizeHint() const + QSize sizeHint() const Q_DECL_OVERRIDE { int width = 150; int height = heightForWidth(width); return QSize(width, height); } - int heightForWidth(int w) const + int heightForWidth(int w) const Q_DECL_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) + void showEvent(QShowEvent* event) Q_DECL_OVERRIDE { QWidget::showEvent(event); layoutRows(); } - void resizeEvent(QResizeEvent* event) + void resizeEvent(QResizeEvent* event) Q_DECL_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() : 0; 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->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(), 0, q, 0); // "Garbage collect" document mDocument = 0; } } }; 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() : 0, GwenviewConfig::preferredMetaInfoKeyList()); d->mImageMetaInfoDialog->show(); } void InfoContextManagerItem::slotPreferredMetaInfoKeyListChanged(const QStringList& list) { GwenviewConfig::setPreferredMetaInfoKeyList(list); GwenviewConfig::self()->save(); updateOneFileInfo(); } } // namespace diff --git a/app/semanticinfocontextmanageritem.cpp b/app/semanticinfocontextmanageritem.cpp index c5f8e914..745feaf6 100644 --- a/app/semanticinfocontextmanageritem.cpp +++ b/app/semanticinfocontextmanageritem.cpp @@ -1,469 +1,469 @@ // 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*) + void paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*) Q_DECL_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")); q->setWidget(mGroup); EventWatcher::install(mGroup, QEvent::Show, q, SLOT(update())); QWidget* container = new QWidget; setupUi(container); container->layout()->setMargin(0); mGroup->addWidget(container); 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")); 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)); 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); 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/sidebar.cpp b/app/sidebar.cpp index c0ce01bb..f55c2f7e 100644 --- a/app/sidebar.cpp +++ b/app/sidebar.cpp @@ -1,246 +1,246 @@ /* 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 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: - virtual void paintEvent(QPaintEvent* event) + void paintEvent(QPaintEvent* event) Q_DECL_OVERRIDE { forceIcon(); QToolButton::paintEvent(event); } - virtual QSize sizeHint() const + QSize sizeHint() const Q_DECL_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; }; SideBarGroup::SideBarGroup(const QString& title) : QFrame() , d(new SideBarGroupPrivate) { d->mContainer = 0; d->mTitleLabel = new QLabel(this); 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); 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); } 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); } SideBar::~SideBar() { delete d; } QSize SideBar::sizeHint() const { return QSize(200, 200); } void SideBar::addPage(SideBarPage* page) { 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); } } } } // namespace diff --git a/app/sidebar.h b/app/sidebar.h index f6404505..0a4b17b5 100644 --- a/app/sidebar.h +++ b/app/sidebar.h @@ -1,88 +1,88 @@ /* 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 { class SideBar; struct SideBarGroupPrivate; class SideBarGroup : public QFrame { Q_OBJECT public: SideBarGroup(const QString& title); ~SideBarGroup(); void addWidget(QWidget*); void addAction(QAction*); void clear(); protected: - virtual void paintEvent(QPaintEvent*); + void paintEvent(QPaintEvent*) Q_DECL_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: SideBar(QWidget* parent); ~SideBar(); void addPage(SideBarPage*); QString currentPage() const; void setCurrentPage(const QString& name); - virtual QSize sizeHint() const; + QSize sizeHint() const Q_DECL_OVERRIDE; private: SideBarPrivate* const d; }; } // namespace #endif /* SIDEBAR_H */ diff --git a/app/splitter.h b/app/splitter.h index f4e90b16..c3ff0b80 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: - virtual void paintEvent(QPaintEvent* event) + void paintEvent(QPaintEvent* event) Q_DECL_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: - virtual QSplitterHandle* createHandle() + QSplitterHandle* createHandle() Q_DECL_OVERRIDE { return new SplitterHandle(orientation(), this); } }; } // namespace #endif /* SPLITTER_H */ diff --git a/app/startmainpage.cpp b/app/startmainpage.cpp index 1622cf70..a7087dae 100644 --- a/app/startmainpage.cpp +++ b/app/startmainpage.cpp @@ -1,313 +1,313 @@ // 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 #ifndef GWENVIEW_SEMANTICINFO_BACKEND_NONE #include #endif namespace Gwenview { class HistoryThumbnailViewHelper : public AbstractThumbnailViewHelper { public: HistoryThumbnailViewHelper(QObject* parent) : AbstractThumbnailViewHelper(parent) {} - virtual void showContextMenu(QWidget*) + void showContextMenu(QWidget*) Q_DECL_OVERRIDE { } - virtual void showMenuForUrlDroppedOnViewport(QWidget*, const QList&) + void showMenuForUrlDroppedOnViewport(QWidget*, const QList&) Q_DECL_OVERRIDE { } - virtual void showMenuForUrlDroppedOnDir(QWidget*, const QList&, const QUrl&) + void showMenuForUrlDroppedOnDir(QWidget*, const QList&, const QUrl&) Q_DECL_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 = 0; 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 folder view connect(d->mRecentFoldersView, &Gwenview::ThumbnailView::indexActivated, this, &StartMainPage::slotListViewActivated); connect(d->mRecentFoldersView, &Gwenview::ThumbnailView::customContextMenuRequested, this, &StartMainPage::showRecentFoldersViewContextMenu); connect(d->mRecentFilesView, &Gwenview::ThumbnailView::indexActivated, this, &StartMainPage::slotListViewActivated); 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::showRecentFoldersViewContextMenu(const QPoint& pos) { QAbstractItemView* view = qobject_cast(sender()); QUrl url; QModelIndex index = view->indexAt(pos); if (index.isValid()) { QVariant data = index.data(KFilePlacesModel::UrlRole); url = data.toUrl(); } // Create menu QMenu menu(this); QAction* addToPlacesAction = menu.addAction(QIcon::fromTheme("bookmark-new"), i18n("Add to Places")); QAction* removeAction = menu.addAction(QIcon::fromTheme("edit-delete"), i18n("Forget this Folder")); menu.addSeparator(); QAction* clearAction = menu.addAction(QIcon::fromTheme("edit-delete-all"), i18n("Forget All")); if (!index.isValid()) { if (addToPlacesAction) { addToPlacesAction->setEnabled(false); } removeAction->setEnabled(false); } // Handle menu QAction* action = menu.exec(view->mapToGlobal(pos)); if (!action) { return; } if (action == addToPlacesAction) { QString text = url.fileName(); if (text.isEmpty()) { text = url.toDisplayString(); } d->mBookmarksModel->addPlace(text, url); } else if (action == removeAction) { view->model()->removeRow(index.row()); } else if (action == clearAction) { view->model()->removeRows(0, view->model()->rowCount()); } } void StartMainPage::loadConfig() { d->updateHistoryTab(); applyPalette(d->mGvCore->palette(GvCore::NormalViewPalette)); } ThumbnailView* StartMainPage::recentFoldersView() const { return d->mRecentFoldersView; } } // namespace diff --git a/app/viewmainpage.h b/app/viewmainpage.h index c8a6fa24..d2b2bae6 100644 --- a/app/viewmainpage.h +++ b/app/viewmainpage.h @@ -1,153 +1,153 @@ /* 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 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(); ThumbnailBarView* thumbnailBar() const; void loadConfig(); void saveConfig(); /** * Reset the view */ void reset(); void setFullScreenMode(bool fullScreen); bool isFullScreenMode() const; int statusBarHeight() const; - virtual QSize sizeHint() const; + QSize sizeHint() const Q_DECL_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 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 trashView(DocumentView*); void deselectView(DocumentView*); private: friend struct ViewMainPagePrivate; ViewMainPagePrivate* const d; }; } // namespace #endif /* VIEWMAINPAGE_H */ diff --git a/importer/importdialog.h b/importer/importdialog.h index 4abe82fc..3ee3134f 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(); - virtual QSize sizeHint() const; + QSize sizeHint() const Q_DECL_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/thumbnailpage.cpp b/importer/thumbnailpage.cpp index 0d2b7775..25a11b2e 100644 --- a/importer/thumbnailpage.cpp +++ b/importer/thumbnailpage.cpp @@ -1,458 +1,458 @@ // 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" // 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*) + void showContextMenu(QWidget*) Q_DECL_OVERRIDE {} - void showMenuForUrlDroppedOnViewport(QWidget*, const QList&) + void showMenuForUrlDroppedOnViewport(QWidget*, const QList&) Q_DECL_OVERRIDE {} - void showMenuForUrlDroppedOnDir(QWidget*, const QList&, const QUrl&) + void showMenuForUrlDroppedOnDir(QWidget*, const QList&, const QUrl&) Q_DECL_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 = 0; 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); delegate->setContextBarActions(PreviewItemDelegate::SelectionAction); 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() { ImporterConfigDialog 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 { 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 { 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/abstractimageoperation.cpp b/lib/abstractimageoperation.cpp index 0dbfc82b..23ecf877 100644 --- a/lib/abstractimageoperation.cpp +++ b/lib/abstractimageoperation.cpp @@ -1,114 +1,114 @@ // 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 // 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; } - virtual void undo() + void undo() Q_DECL_OVERRIDE { mOp->undo(); } private: AbstractImageOperation* mOp; }; struct AbstractImageOperationPrivate { QString mText; QUrl mUrl; }; AbstractImageOperation::AbstractImageOperation() : d(new AbstractImageOperationPrivate) { } AbstractImageOperation::~AbstractImageOperation() { delete d; } void AbstractImageOperation::applyToDocument(Document::Ptr doc) { d->mUrl = doc->url(); redo(); } Document::Ptr AbstractImageOperation::document() const { Document::Ptr doc = DocumentFactory::instance()->load(d->mUrl); doc->startLoadingFullImage(); return doc; } void AbstractImageOperation::finish(bool ok) { if (ok) { ImageOperationCommand* command = new ImageOperationCommand(this); command->setText(d->mText); document()->undoStack()->push(command); } else { deleteLater(); } } 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/binder.h b/lib/binder.h index b5573ba7..9b1750b3 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() + void callMethod() Q_DECL_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 7822d1e3..3f7ae8c4 100644 --- a/lib/crop/cropimageoperation.cpp +++ b/lib/crop/cropimageoperation.cpp @@ -1,94 +1,94 @@ // 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) {} - virtual void threadedStart() + void threadedStart() Q_DECL_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); } } // namespace diff --git a/lib/documentview/videoviewadapter.cpp b/lib/documentview/videoviewadapter.cpp index bcfaf8e9..b5af9b5f 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) + void mousePressEvent(QGraphicsSceneMouseEvent* event) Q_DECL_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")); } else { d->mPlayPauseAction->setIcon(QIcon::fromTheme("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") ); } 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/fullscreenbar.h b/lib/fullscreenbar.h index 3ecbf468..0ecaded3 100644 --- a/lib/fullscreenbar.h +++ b/lib/fullscreenbar.h @@ -1,70 +1,70 @@ // 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: FullScreenBar(QWidget* parent); ~FullScreenBar(); void setActivated(bool); void setAutoHidingEnabled(bool); - virtual QSize sizeHint() const; + QSize sizeHint() const Q_DECL_OVERRIDE; public Q_SLOTS: void slideIn(); void slideOut(); private Q_SLOTS: void slotAutoHideCursorTimeout(); void moveBar(qreal); void delayedInstallEventFilter(); protected: - virtual bool eventFilter(QObject*, QEvent*); + bool eventFilter(QObject*, QEvent*) Q_DECL_OVERRIDE; private: FullScreenBarPrivate* const d; }; } // namespace #endif /* FULLSCREENBAR_H */ diff --git a/lib/historymodel.cpp b/lib/historymodel.cpp index 2e698328..c89e6742 100644 --- a/lib/historymodel.cpp +++ b/lib/historymodel.cpp @@ -1,252 +1,252 @@ // 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 // 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 0; } QTemporaryFile file(storageDir + QStringLiteral("/gvhistoryXXXXXXrc")); file.setAutoRemove(false); if (!file.open()) { qCritical() << "Could not create history file"; return 0; } 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 0; } QDateTime dateTime = QDateTime::fromString(group.readEntry("dateTime"), Qt::ISODate); if (!dateTime.isValid()) { qCritical() << "Invalid dateTime" << dateTime; return 0; } 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) { setText(mUrl.toDisplayString()); 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 { + bool operator<(const QStandardItem& other) const Q_DECL_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")) { 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/redeyereduction/redeyereductionimageoperation.cpp b/lib/redeyereduction/redeyereductionimageoperation.cpp index 9e5e9308..905a9816 100644 --- a/lib/redeyereduction/redeyereductionimageoperation.cpp +++ b/lib/redeyereduction/redeyereductionimageoperation.cpp @@ -1,166 +1,166 @@ // 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() + void threadedStart() Q_DECL_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); } /** * 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/resize/resizeimageoperation.cpp b/lib/resize/resizeimageoperation.cpp index a31c64c7..dbc3f745 100644 --- a/lib/resize/resizeimageoperation.cpp +++ b/lib/resize/resizeimageoperation.cpp @@ -1,94 +1,94 @@ // 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) {} - virtual void threadedStart() + void threadedStart() Q_DECL_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); } } // namespace diff --git a/lib/semanticinfo/tagwidget.cpp b/lib/semanticinfo/tagwidget.cpp index 3a0b587c..ddd4c1c7 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: - virtual bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const + bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const Q_DECL_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: ReturnKeyEater(QObject* parent = 0) : QObject(parent) {} protected: - virtual bool eventFilter(QObject*, QEvent* event) + bool eventFilter(QObject*, QEvent* event) Q_DECL_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 = 0; 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/thumbnailview/thumbnailbarview.h b/lib/thumbnailview/thumbnailbarview.h index cb5252d6..1ebc9b31 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(); virtual void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const Q_DECL_OVERRIDE; virtual QSize sizeHint(const QStyleOptionViewItem & /*option*/, const QModelIndex & /*index*/) const Q_DECL_OVERRIDE; protected: virtual bool eventFilter(QObject*, QEvent*) Q_DECL_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: - virtual void resizeEvent(QResizeEvent * event); - virtual void wheelEvent(QWheelEvent* event); - virtual void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected); + 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; private Q_SLOTS: void slotFrameChanged(int); private: ThumbnailBarViewPrivate* const d; }; } // namespace #endif /* THUMBNAILBARVIEW_H */ diff --git a/lib/widgetfloater.h b/lib/widgetfloater.h index 2f91b597..5ba876ff 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: WidgetFloater(QWidget* parent); ~WidgetFloater(); void setChildWidget(QWidget*); void setAlignment(Qt::Alignment); void setHorizontalMargin(int); int horizontalMargin() const; void setVerticalMargin(int); int verticalMargin() const; protected: - bool eventFilter(QObject*, QEvent*); + bool eventFilter(QObject*, QEvent*) Q_DECL_OVERRIDE; private: WidgetFloaterPrivate* const d; }; } // namespace #endif /* WIDGETFLOATER_H */ diff --git a/tests/auto/contextmanagertest.cpp b/tests/auto/contextmanagertest.cpp index 53e6c8c2..ade7775e 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")); // 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")); } void ContextManagerTest::testInvalidDirUrl() { class DirLister : public KDirLister { public: DirLister() : mOpenUrlCalled(false) { setAutoErrorHandlingEnabled(false, 0); } - bool openUrl(const QUrl &url, OpenUrlFlags flags = NoFlags) + bool openUrl(const QUrl &url, OpenUrlFlags flags = NoFlags) Q_DECL_OVERRIDE { mOpenUrlCalled = true; return KDirLister::openUrl(url, flags); } bool mOpenUrlCalled; }; SortedDirModel dirModel; DirLister* dirLister = new DirLister; dirModel.setDirLister(dirLister); ContextManager manager(&dirModel, 0); manager.setCurrentDirUrl(QUrl()); QVERIFY(!dirLister->mOpenUrlCalled); }