diff options
| author | Silarn <jrim@rimpo.org> | 2019-07-22 01:00:42 -0500 |
|---|---|---|
| committer | Silarn <jrim@rimpo.org> | 2019-07-22 01:00:42 -0500 |
| commit | dcd6d624672019727d7effd17aac86f72bff438b (patch) | |
| tree | 1e8d3856f657d898c5992631599cf272d785f973 /src/modinfodialogimages.cpp | |
| parent | 179a73857125ee604f42b0d5c2d765183c86d2c7 (diff) | |
| parent | e73c309f08eff98f0dbd2590f594a83b67431eac (diff) | |
Merge branch 'Develop'
Diffstat (limited to 'src/modinfodialogimages.cpp')
| -rw-r--r-- | src/modinfodialogimages.cpp | 1102 |
1 files changed, 1102 insertions, 0 deletions
diff --git a/src/modinfodialogimages.cpp b/src/modinfodialogimages.cpp new file mode 100644 index 00000000..69866902 --- /dev/null +++ b/src/modinfodialogimages.cpp @@ -0,0 +1,1102 @@ +#include "modinfodialogimages.h" +#include "ui_modinfodialog.h" +#include "settings.h" +#include "utility.h" + +using namespace ImagesTabHelpers; + +QSize resizeWithAspectRatio(const QSize& original, const QSize& available) +{ + const auto ratio = std::min({ + 1.0, + static_cast<double>(available.width()) / original.width(), + static_cast<double>(available.height()) / original.height()}); + + const QSize scaledSize( + static_cast<int>(std::round(original.width() * ratio)), + static_cast<int>(std::round(original.height() * ratio))); + + return scaledSize; +} + +QRect centeredRect(const QRect& rect, const QSize& size) +{ + return QRect( + (rect.left()+rect.width()/2) - size.width()/2, + (rect.top()+rect.height()/2) - size.height()/2, + size.width(), + size.height()); +} + +QString dimensionString(const QSize& s) +{ + return QString::fromUtf8("%1 \xc3\x97 %2") + .arg(s.width()).arg(s.height()); +} + + +ImagesTab::ImagesTab(ModInfoDialogTabContext cx) : + ModInfoDialogTab(std::move(cx)), m_image(new ScalableImage), + m_ddsAvailable(false), m_ddsEnabled(false) +{ + getSupportedFormats(); + + auto* ly = new QVBoxLayout(ui->imagesImage); + ly->setContentsMargins({0, 0, 0, 0}); + ly->addWidget(m_image); + + delete ui->imagesThumbnails->layout(); + + ui->tabImagesSplitter->setSizes({128, 1}); + ui->tabImagesSplitter->setStretchFactor(0, 0); + ui->tabImagesSplitter->setStretchFactor(1, 1); + + ui->imagesThumbnails->setTab(this); + + ui->imagesScrollerVBar->setTab(this); + connect(ui->imagesScrollerVBar, &QScrollBar::valueChanged, [&]{ onScrolled(); }); + + ui->imagesShowDDS->setEnabled(m_ddsAvailable); + + m_filter.setEdit(ui->imagesFilter); + connect(&m_filter, &FilterWidget::changed, [&]{ onFilterChanged(); }); + + connect(ui->imagesExplore, &QAbstractButton::clicked, [&]{ onExplore(); }); + connect(ui->imagesShowDDS, &QCheckBox::toggled, [&]{ onShowDDS(); }); + + ui->imagesShowDDS->setEnabled(m_ddsAvailable); + + ui->imagesThumbnails->setAutoFillBackground(false); + ui->imagesThumbnails->setAttribute(Qt::WA_OpaquePaintEvent, true); + + { + auto list = std::make_unique<QListWidget>(); + parentWidget()->style()->polish(list.get()); + + m_theme.borderColor = QColor(Qt::black); + m_theme.backgroundColor = QColor(Qt::black); + m_theme.textColor = list->palette().color(QPalette::WindowText); + + m_theme.highlightBackgroundColor = list->palette().color(QPalette::Highlight); + m_theme.highlightTextColor = list->palette().color(QPalette::HighlightedText); + + m_theme.font = list->font(); + + const QFontMetrics fm(m_theme.font); + m_metrics.textHeight = fm.height(); + + m_image->setColors(m_theme.borderColor, m_theme.backgroundColor); + } +} + +void ImagesTab::clear() +{ + m_files.clear(); + ui->imagesScrollerVBar->setValue(0); + select(BadIndex); + setHasData(false); +} + +bool ImagesTab::feedFile(const QString& rootPath, const QString& fullPath) +{ + for (const auto& ext : m_supportedFormats) { + if (fullPath.endsWith(ext, Qt::CaseInsensitive)) { + m_files.add({fullPath}); + return true; + } + } + + return false; +} + +void ImagesTab::update() +{ + checkFiltering(); + updateScrollbar(); + + // visibility needs to be rechecked here because the scrollbar configuration + // may have changed in updateScrollbar(), in which case any ensureVisible() + // calls in checkFiltering() might have been incorrect + if (m_files.selectedIndex() != BadIndex) { + ensureVisible(m_files.selectedIndex(), Visibility::Partial); + } + + ui->imagesThumbnails->update(); + + setHasData(m_files.size() > 0); +} + +void ImagesTab::saveState(Settings& s) +{ + s.directInterface().setValue( + "mod_info_dialog_images_show_dds", m_ddsEnabled); + + saveWidgetState(s.directInterface(), ui->tabImagesSplitter); +} + +void ImagesTab::restoreState(const Settings& s) +{ + ui->imagesShowDDS->setChecked(s.directInterface() + .value("mod_info_dialog_images_show_dds", false).toBool()); + + restoreWidgetState(s.directInterface(), ui->tabImagesSplitter); +} + +void ImagesTab::checkFiltering() +{ + if (m_filter.empty() && m_ddsEnabled) { + // no filtering needed + + if (m_files.isFiltered()) { + // was filtered, needs switch + switchToAll(); + } + } else { + // filtering is needed + switchToFiltered(); + } +} + +void ImagesTab::switchToAll() +{ + // remember selection + const auto* oldSelection = m_files.selectedFile(); + + // switch + m_files.switchToAll(); + + // reselect old + if (oldSelection) { + select(m_files.indexOf(oldSelection)); + } else { + select(BadIndex); + } +} + +void ImagesTab::switchToFiltered() +{ + // remember old selection, will be checked when building the filtered list + // below + const auto* oldSelection = m_files.selectedFile(); + std::size_t newSelection = BadIndex; + + // switch, also clears list + m_files.switchToFiltered(); + + const bool hasTextFilter = !m_filter.empty(); + + for (auto& f : m_files.allFiles()) { + if (hasTextFilter) { + // check filter widget + const auto m = m_filter.matches([&](auto&& what) { + return f.path().contains(what, Qt::CaseInsensitive); + }); + + if (!m) { + // no match, skip + continue; + } + } + + if (!m_ddsEnabled) { + // skip .dds files + if (f.path().endsWith(".dds", Qt::CaseInsensitive)) { + continue; + } + } + + if (&f == oldSelection) { + // found the old selection, remember its index + newSelection = m_files.size(); + } + + m_files.addFiltered(&f); + } + + // reselect old, or clear if it wasn't found + select(newSelection); +} + +void ImagesTab::getSupportedFormats() +{ + m_ddsAvailable = false; + + for (const auto& entry : QImageReader::supportedImageFormats()) { + QString s(entry); + if (s.isNull() || s.isEmpty()) { + continue; + } + + // used to enable the checkbox + if (s.compare("dds", Qt::CaseInsensitive) == 0) { + m_ddsAvailable = true; + } + + // make sure it starts with a dot + if (s[0] != ".") { + s = "." + s; + } + + m_supportedFormats.emplace_back(std::move(s)); + } +} + +void ImagesTab::select(std::size_t i, Visibility v) +{ + m_files.select(i); + + if (auto* f=m_files.selectedFile()) { + // when jumping elsewhere in the list, such as with page down/up, the file + // might not be visible yet, which means it hasn't been loaded and would + // pass a null image in setImage() below + f->ensureOriginalLoaded(); + + ui->imagesPath->setText(QDir::toNativeSeparators(f->path())); + ui->imagesExplore->setEnabled(true); + ui->imagesSize->setText(dimensionString(f->original().size())); + + m_image->setImage(f->original()); + ensureVisible(i, v); + } else { + ui->imagesPath->clear(); + ui->imagesExplore->setEnabled(false); + ui->imagesSize->clear(); + m_image->clear(); + } + + ui->imagesThumbnails->update(); +} + +void ImagesTab::moveSelection(int by) +{ + if (m_files.empty()) { + return; + } + + auto i = m_files.selectedIndex(); + if (i == BadIndex) { + i = 0; + } + + if (by > 0) { + // moving down + i += static_cast<std::size_t>(by); + + if (i >= m_files.size()) { + i = (m_files.size() - 1); + } + } else if (by < 0) { + // moving up + const auto abs_by = static_cast<std::size_t>(std::abs(by)); + + if (abs_by > i) { + i = 0; + } else { + i -= abs_by; + } + } + + select(i); +} + +void ImagesTab::ensureVisible(std::size_t i, Visibility v) +{ + if (v == Visibility::Ignore) { + return; + } + + const auto geo = makeGeometry(); + + const auto fullyVisible = geo.fullyVisibleCount(); + const auto partiallyVisible = fullyVisible + 1; + + const auto first = ui->imagesScrollerVBar->value(); + const auto last = (v == Visibility::Full ? + first + fullyVisible : first + partiallyVisible); + + if (i < first) { + // go up + ui->imagesScrollerVBar->setValue(static_cast<int>(i)); + } else if (i >= last) { + // go down + + if (i >= fullyVisible) { + ui->imagesScrollerVBar->setValue(static_cast<int>(i - fullyVisible + 1)); + } + } +} + +std::size_t ImagesTab::fileIndexAtPos(const QPoint& p) const +{ + const auto geo = makeGeometry(); + + // this is the index relative to the top + const auto offset = geo.indexAt(p); + if (offset == BadIndex) { + return BadIndex; + } + + const auto first = ui->imagesScrollerVBar->value(); + if (first < 0) { + return BadIndex; + } + + const auto i = static_cast<std::size_t>(first) + offset; + if (i >= m_files.size()) { + return BadIndex; + } + + return i; +} + +const File* ImagesTab::fileAtPos(const QPoint& p) const +{ + const auto i = fileIndexAtPos(p); + if (i >= m_files.size()) { + return nullptr; + } + + return m_files.get(i); +} + +Geometry ImagesTab::makeGeometry() const +{ + return Geometry(ui->imagesThumbnails->size(), m_metrics); +} + +void ImagesTab::paintThumbnailsArea(QPaintEvent* e) +{ + PaintContext cx(ui->imagesThumbnails, makeGeometry()); + + cx.painter.fillRect( + ui->imagesThumbnails->rect(), + ui->imagesThumbnails->palette().color(QPalette::Window)); + + const auto visible = cx.geo.fullyVisibleCount() + 1; + const auto first = ui->imagesScrollerVBar->value(); + + for (std::size_t i=0; i<visible; ++i) { + const auto fileIndex = first + i; + auto* file = m_files.get(fileIndex); + if (!file) { + break; + } + + cx.file = file; + cx.thumbIndex = i; + cx.fileIndex = fileIndex; + + paintThumbnail(cx); + } +} + +void ImagesTab::paintThumbnail(const PaintContext& cx) +{ + paintThumbnailBackground(cx); + paintThumbnailBorder(cx); + paintThumbnailImage(cx); + paintThumbnailText(cx); +} + +void ImagesTab::paintThumbnailBackground(const PaintContext& cx) +{ + if (m_files.selectedIndex() == cx.fileIndex) { + const auto rect = cx.geo.thumbRect(cx.thumbIndex); + cx.painter.fillRect(rect, m_theme.highlightBackgroundColor); + } +} + +void ImagesTab::paintThumbnailBorder(const PaintContext& cx) +{ + auto borderRect = cx.geo.borderRect(cx.thumbIndex); + + // rects don't include the bottom right corner, but drawRect() does, so + // resize it + borderRect.setRight(borderRect.right() - 1); + borderRect.setBottom(borderRect.bottom() - 1); + + cx.painter.setPen(m_theme.borderColor); + cx.painter.drawRect(borderRect); +} + +void ImagesTab::paintThumbnailImage(const PaintContext& cx) +{ + if (cx.file->failed()) { + return; + } + + cx.file->loadIfNeeded(cx.geo); + + const auto imageRect = cx.geo.imageRect(cx.thumbIndex); + const auto scaledThumbRect = centeredRect( + imageRect, cx.file->thumbnail().size()); + + cx.painter.fillRect(scaledThumbRect, m_theme.backgroundColor); + cx.painter.drawImage(scaledThumbRect, cx.file->thumbnail()); +} + +void ImagesTab::paintThumbnailText(const PaintContext& cx) +{ + const auto tr = cx.geo.textRect(cx.thumbIndex); + + if (cx.fileIndex == m_files.selectedIndex()) { + cx.painter.setPen(m_theme.highlightTextColor); + } else { + cx.painter.setPen(m_theme.textColor); + } + + cx.painter.setFont(m_theme.font); + + QFontMetrics fm(m_theme.font); + + const auto text = fm.elidedText( + cx.file->filename(), Qt::ElideRight, tr.width()); + + const auto flags = Qt::AlignHCenter | Qt::AlignVCenter | Qt::TextSingleLine; + + cx.painter.drawText(tr, flags, text); +} + +void ImagesTab::scrollAreaResized(const QSize&) +{ + updateScrollbar(); +} + +void ImagesTab::thumbnailAreaMouseEvent(QMouseEvent* e) +{ + if (e->button() != Qt::LeftButton) { + return; + } + + const auto i = fileIndexAtPos(e->pos()); + if (i != BadIndex) { + // the only way to click on a thumbnail is if it's already visible, so the + // only thing that can happen is a click on a partially visible thumbnail, + // which would scroll so it is fully visible, and that's just annoying + select(i, Visibility::Ignore); + } +} + +void ImagesTab::thumbnailAreaWheelEvent(QWheelEvent* e) +{ + const auto d = (e->angleDelta() / 8).y(); + + ui->imagesScrollerVBar->setValue( + ui->imagesScrollerVBar->value() + (d > 0 ? -1 : 1)); +} + +bool ImagesTab::thumbnailAreaKeyPressEvent(QKeyEvent* e) +{ + switch (e->key()) { + case Qt::Key_Down: { + moveSelection(ui->imagesScrollerVBar->singleStep()); + return true; + } + + case Qt::Key_Up: { + moveSelection(-ui->imagesScrollerVBar->singleStep()); + return true; + } + + case Qt::Key_PageDown: { + moveSelection(ui->imagesScrollerVBar->pageStep()); + return true; + } + + case Qt::Key_PageUp: { + moveSelection(-ui->imagesScrollerVBar->pageStep()); + return true; + } + + case Qt::Key_Home: { + select(0); + return true; + } + + case Qt::Key_End: { + if (!m_files.empty()) { + select(m_files.size() - 1); + } + + return true; + } + } + + return false; +} + +void ImagesTab::onScrolled() +{ + ui->imagesThumbnails->update(); +} + +void ImagesTab::showTooltip(QHelpEvent* e) +{ + const auto* f = fileAtPos(e->pos()); + if (!f) { + QToolTip::hideText(); + e->ignore(); + return; + } + + const auto s = QString("%1 (%2)") + .arg(QDir::toNativeSeparators(f->path())) + .arg(dimensionString(f->original().size())); + + QToolTip::showText(e->globalPos(), s, ui->imagesThumbnails); +} + +void ImagesTab::onExplore() +{ + if (auto* f=m_files.selectedFile()) { + MOBase::shell::ExploreFile(f->path()); + } +} + +void ImagesTab::onShowDDS() +{ + const auto b = ui->imagesShowDDS->isChecked(); + if (b != m_ddsEnabled) { + m_ddsEnabled = b; + update(); + } +} + +void ImagesTab::onFilterChanged() +{ + update(); +} + +void ImagesTab::updateScrollbar() +{ + if (m_files.size() == 0) { + ui->imagesScrollerVBar->setRange(0, 0); + ui->imagesScrollerVBar->setEnabled(false); + return; + } + + const auto geo = makeGeometry(); + const auto availableSize = ui->imagesThumbnails->size(); + const auto fullyVisible = geo.fullyVisibleCount(); + + if (fullyVisible >= m_files.size()) { + ui->imagesScrollerVBar->setRange(0, 0); + ui->imagesScrollerVBar->setEnabled(false); + } else { + const auto d = m_files.size() - fullyVisible; + ui->imagesScrollerVBar->setRange(0, static_cast<int>(d)); + ui->imagesScrollerVBar->setSingleStep(1); + ui->imagesScrollerVBar->setPageStep(static_cast<int>(fullyVisible - 1)); + ui->imagesScrollerVBar->setEnabled(true); + } +} + + +namespace ImagesTabHelpers +{ + +void Scrollbar::setTab(ImagesTab* tab) +{ + m_tab = tab; +} + +void Scrollbar::wheelEvent(QWheelEvent* e) +{ + if (m_tab) { + m_tab->thumbnailAreaWheelEvent(e); + } +} + + +void ThumbnailsWidget::setTab(ImagesTab* tab) +{ + m_tab = tab; +} + +void ThumbnailsWidget::paintEvent(QPaintEvent* e) +{ + if (m_tab) { + m_tab->paintThumbnailsArea(e); + } +} + +void ThumbnailsWidget::mousePressEvent(QMouseEvent* e) +{ + if (m_tab) { + m_tab->thumbnailAreaMouseEvent(e); + } +} + +void ThumbnailsWidget::wheelEvent(QWheelEvent* e) +{ + if (m_tab) { + m_tab->thumbnailAreaWheelEvent(e); + } +} + +void ThumbnailsWidget::resizeEvent(QResizeEvent* e) +{ + if (m_tab) { + m_tab->scrollAreaResized(e->size()); + } +} + +void ThumbnailsWidget::keyPressEvent(QKeyEvent* e) +{ + if (m_tab) { + if (m_tab->thumbnailAreaKeyPressEvent(e)) { + return; + } + } + + QWidget::keyPressEvent(e); +} + +bool ThumbnailsWidget::event(QEvent* e) +{ + if (e->type() == QEvent::ToolTip) { + m_tab->showTooltip(static_cast<QHelpEvent*>(e)); + return true; + } + + return QWidget::event(e); +} + + +ScalableImage::ScalableImage(QString path) + : m_path(std::move(path)), m_border(1) +{ + auto sp = sizePolicy(); + sp.setHeightForWidth(true); + setSizePolicy(sp); +} + +void ScalableImage::setImage(const QString& path) +{ + m_path = path; + m_original = {}; + m_scaled = {}; + + update(); +} + +void ScalableImage::setImage(QImage image) +{ + m_path.clear(); + m_original = std::move(image); + m_scaled = {}; + + update(); +} + +void ScalableImage::clear() +{ + setImage(QImage()); +} + +bool ScalableImage::hasHeightForWidth() const +{ + return true; +} + +int ScalableImage::heightForWidth(int w) const +{ + return w; +} + +void ScalableImage::setColors(const QColor& border, const QColor& background) +{ + m_borderColor = border; + m_backgroundColor = background; +} + +void ScalableImage::paintEvent(QPaintEvent* e) +{ + if (m_original.isNull()) { + if (m_path.isNull()) { + return; + } + + m_original.load(m_path); + + if (m_original.isNull()) { + return; + } + } + + const QRect widgetRect = rect(); + const QRect imageRect = widgetRect.adjusted( + m_border, m_border, -m_border, -m_border); + + const QSize scaledSize = resizeWithAspectRatio( + m_original.size(), imageRect.size()); + + if (m_scaled.isNull() || m_scaled.size() != scaledSize) { + m_scaled = m_original.scaled( + scaledSize.width(), scaledSize.height(), + Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + } + + const QRect drawBorderRect = widgetRect.adjusted(0, 0, -1, -1); + const QRect drawImageRect = centeredRect(imageRect, m_scaled.size()); + + QPainter painter(this); + + // border + painter.setPen(m_borderColor); + painter.drawRect(drawBorderRect); + + // background + painter.fillRect(drawImageRect, m_backgroundColor); + + // image + painter.drawImage(drawImageRect, m_scaled); +} + + +Metrics::Metrics() : + margins(3), + border(1), + padding(0), + spacing(5), + textSpacing(2), + textHeight(0) +{ +} + + +Geometry::Geometry(QSize widgetSize, Metrics metrics) + : m_widgetSize(widgetSize), m_metrics(metrics), m_topRect(calcTopRect()) +{ +} + +QRect Geometry::calcTopRect() const +{ + const auto thumbWidth = m_widgetSize.width(); + const auto& m = m_metrics; + + const auto imageSize = + thumbWidth - + (m.margins * 2) - + (m.border * 2) - + (m.padding * 2); + + const auto thumbHeight = + m.margins + + m.border + m.padding + + imageSize + + m.padding + m.border + + m.textSpacing + m.textHeight + + m.margins; + + return {0, 0, thumbWidth, thumbHeight}; +} + +std::size_t Geometry::fullyVisibleCount() const +{ + const auto r = thumbRect(0); + + const auto thumbWithSpacing = r.height() + m_metrics.spacing; + const auto visible = (m_widgetSize.height() / thumbWithSpacing); + + return static_cast<std::size_t>(visible); +} + +QRect Geometry::thumbRect(std::size_t i) const +{ + // rect for the top thumbnail + QRect r = m_topRect; + + // move down + const auto thumbWithSpacing = m_metrics.spacing + r.height(); + r.translate(0, static_cast<int>(i * thumbWithSpacing)); + + return r; +} + +QRect Geometry::borderRect(std::size_t i) const +{ + auto r = thumbRect(i); + const auto& m = m_metrics; + + // remove margins and text + r.adjust(m.margins, m.margins, -m.margins, -m.margins); + + // remove text + r.adjust(0, 0, 0, -(m.textSpacing + m.textHeight)); + + return r; +} + +QRect Geometry::imageRect(std::size_t i) const +{ + auto r = borderRect(i); + + // remove border and padding + const auto m = m_metrics.border + m_metrics.padding; + r.adjust(m, m, -m, -m); + + return r; +} + +QRect Geometry::textRect(std::size_t i) const +{ + const auto r = borderRect(i); + + return QRect( + r.left(), + r.bottom() + m_metrics.textSpacing, + r.width(), + m_metrics.textHeight); +} + +QRect Geometry::selectionRect(std::size_t i) const +{ + const auto br = borderRect(i); + const auto tr = textRect(i); + + return QRect( + br.left(), + br.top(), + br.width(), + tr.bottom() - br.top()); +} + +std::size_t Geometry::indexAt(const QPoint& p) const +{ + // calculate index purely based on y position + const std::size_t offset = p.y() / (m_topRect.height() + m_metrics.spacing); + + if (!selectionRect(offset).contains(p)) { + return BadIndex; + } + + return offset; +} + +QSize Geometry::scaledImageSize(const QSize& originalSize) const +{ + const auto availableSize = imageRect(0).size(); + return resizeWithAspectRatio(originalSize, availableSize); +} + + +File::File(QString path) + : m_path(std::move(path)), m_failed(false) +{ +} + +void File::ensureOriginalLoaded() +{ + if (!m_original.isNull()) { + // already loaded + return; + } + + QImageReader reader(m_path); + + if (!reader.read(&m_original)) { + qCritical().noquote().nospace() + << "failed to load '" << m_path << "'\n" + << reader.errorString() << " " + << "(error " << static_cast<int>(reader.error()) << ")"; + + m_failed = true; + } +} + +const QString& File::path() const +{ + return m_path; +} + +const QString& File::filename() const +{ + if (m_filename.isEmpty()) { + m_filename = QFileInfo(m_path).fileName(); + } + + return m_filename; +} + +const QImage& File::original() const +{ + return m_original; +} + +const QImage& File::thumbnail() const +{ + return m_thumbnail; +} + +bool File::failed() const +{ + return m_failed; +} + +void File::loadIfNeeded(const Geometry& geo) +{ + if (needsLoad(geo)) { + load(geo); + } +} + +bool File::needsLoad(const Geometry& geo) const +{ + if (m_failed) { + return false; + } + + if (m_original.isNull() || m_thumbnail.isNull()) { + return true; + } + + const auto scaledSize = geo.scaledImageSize(m_original.size()); + return (m_thumbnail.size() != scaledSize); +} + +void File::load(const Geometry& geo) +{ + m_failed = false; + ensureOriginalLoaded(); + + if (m_failed) { + return; + } + + const auto scaledSize = geo.scaledImageSize(m_original.size()); + + m_thumbnail = m_original.scaled( + scaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); +} + + +Files::Files() + : m_selection(BadIndex), m_filtered(false) +{ +} + +void Files::clear() +{ + m_allFiles.clear(); + m_filteredFiles.clear(); + m_selection = BadIndex; + m_filtered = false; +} + +void Files::add(File f) +{ + m_allFiles.emplace_back(std::move(f)); +} + +void Files::addFiltered(File* f) +{ + m_filteredFiles.push_back(f); +} + +bool Files::empty() const +{ + if (m_filtered) { + return m_filteredFiles.empty(); + } else { + return m_allFiles.empty(); + } +} + +std::size_t Files::size() const +{ + if (m_filtered) { + return m_filteredFiles.size(); + } else { + return m_allFiles.size(); + } +} + +void Files::switchToAll() +{ + m_filtered = false; + m_filteredFiles.clear(); +} + +void Files::switchToFiltered() +{ + m_filtered = true; + m_filteredFiles.clear(); +} + +const File* Files::get(std::size_t i) const +{ + if (m_filtered) { + if (i < m_filteredFiles.size()) { + return m_filteredFiles[i]; + } + } else { + if (i < m_allFiles.size()) { + return &m_allFiles[i]; + } + } + + return nullptr; +} + +File* Files::get(std::size_t i) +{ + return const_cast<File*>(std::as_const(*this).get(i)); +} + +std::size_t Files::indexOf(const File* f) const +{ + if (m_filtered) { + for (std::size_t i=0; i<m_filteredFiles.size(); ++i) { + if (m_filteredFiles[i] == f) { + return i; + } + } + } else { + for (std::size_t i=0; i<m_allFiles.size(); ++i) { + if (&m_allFiles[i] == f) { + return i; + } + } + } + + return BadIndex; +} + +const File* Files::selectedFile() const +{ + return get(m_selection); +} + +File* Files::selectedFile() +{ + return get(m_selection); +} + +std::size_t Files::selectedIndex() const +{ + return m_selection; +} + +void Files::select(std::size_t i) +{ + m_selection = i; +} + +std::vector<File>& Files::allFiles() +{ + return m_allFiles; +} + +bool Files::isFiltered() const +{ + return m_filtered; +} + + +PaintContext::PaintContext(QWidget* w, Geometry geo) + : painter(w), geo(geo), file(nullptr), thumbIndex(0), fileIndex(0) +{ +} + +} // namespace |
