diff --git a/app/decoder/ffmpeg/ffmpegdecoder.cpp b/app/decoder/ffmpeg/ffmpegdecoder.cpp index 2af558164..acfbc0dc3 100644 --- a/app/decoder/ffmpeg/ffmpegdecoder.cpp +++ b/app/decoder/ffmpeg/ffmpegdecoder.cpp @@ -169,6 +169,10 @@ bool FFmpegDecoder::Probe(Footage *f) // Retrieve metadata about the media av_dump_format(fmt_ctx_, stream()->index(), filename, 0); + // Set duration + f->set_duration(rational(fmt_ctx_->duration, AV_TIME_BASE)); + qDebug() << "Found duration:" << f->duration().ToDouble(); + // Dump it into the Footage object for (unsigned int i=0;inb_streams;i++) { diff --git a/app/project/item/footage/footage.cpp b/app/project/item/footage/footage.cpp index 36ae6b9d2..a6658164c 100644 --- a/app/project/item/footage/footage.cpp +++ b/app/project/item/footage/footage.cpp @@ -77,6 +77,16 @@ void Footage::set_timestamp(const QDateTime &t) timestamp_ = t; } +const rational &Footage::duration() +{ + return duration_; +} + +void Footage::set_duration(const rational &duration) +{ + duration_ = duration; +} + void Footage::add_stream(Stream *s) { // Add a copy of this stream to the list @@ -170,7 +180,7 @@ void Footage::UpdateTooltip() break; case kReady: { - QString tip = QCoreApplication::translate("Footage", "Filename: %1"); + QString tip = QCoreApplication::translate("Footage", "Filename: %1").arg(filename()); if (!streams_.isEmpty()) { tip.append("\n"); diff --git a/app/project/item/footage/footage.h b/app/project/item/footage/footage.h index 0d2d86cfc..ccaefad00 100644 --- a/app/project/item/footage/footage.h +++ b/app/project/item/footage/footage.h @@ -27,6 +27,7 @@ #include "project/item/item.h" #include "project/item/footage/audiostream.h" #include "project/item/footage/videostream.h" +#include "rational.h" class Footage : public Item { @@ -137,6 +138,10 @@ public: */ void set_timestamp(const QDateTime& t); + const rational& duration(); + + void set_duration(const rational& duration); + /** * @brief Add a stream metadata object to this footage * @@ -226,6 +231,11 @@ private: * @brief Internal ready setting */ Status status_; + + /** + * @brief Media duration + */ + rational duration_; }; #endif // FOOTAGE_H diff --git a/app/project/item/footage/stream.h b/app/project/item/footage/stream.h index 555e336a8..bcdd0c2b0 100644 --- a/app/project/item/footage/stream.h +++ b/app/project/item/footage/stream.h @@ -25,6 +25,15 @@ class Footage; +/** + * @brief The Stream class + * + * A base class for keeping metadata about a media stream. A Stream can contain video data, audio data, subtitle data, + * etc. and a Stream object stores metadata about it. + * + * The Stream class is fairly simple and is intended to be subclassed for data that pertains specifically to one + * Stream::Type. \see VideoStream and \see AudioStream. + */ class Stream { public: diff --git a/app/rational.cpp b/app/rational.cpp index e60e2c113..f08008e32 100644 --- a/app/rational.cpp +++ b/app/rational.cpp @@ -324,6 +324,16 @@ double rational::ToDouble() const } } +const int64_t &rational::numerator() +{ + return numerator_; +} + +const int64_t &rational::denominator() +{ + return denominator_; +} + void rational::FixSigns() { // Ensures denominator is always positive (while numerator can be positive or negative) diff --git a/app/rational.h b/app/rational.h index c545d1fa7..bbac6d00d 100644 --- a/app/rational.h +++ b/app/rational.h @@ -84,6 +84,10 @@ public: // Convert to double double ToDouble() const; + + // Specific values + const int64_t& numerator(); + const int64_t& denominator(); private: int64_t numerator_; int64_t denominator_; diff --git a/app/task/import/import.h b/app/task/import/import.h index 4ed22f9d9..1d294841c 100644 --- a/app/task/import/import.h +++ b/app/task/import/import.h @@ -24,6 +24,14 @@ #include "project/item/folder/folder.h" #include "task/task.h" +/** + * @brief The ImportTask class + * + * A background task to create Footage objects from a list of URLs, and then create ProbeTasks for each of them. + * + * Using this Task is the best way to import media into a project since it will run in the background/multithreaded + * without pausing the main thread. + */ class ImportTask : public Task { Q_OBJECT diff --git a/app/task/probe/probe.h b/app/task/probe/probe.h index c0244aceb..0841f0dd0 100644 --- a/app/task/probe/probe.h +++ b/app/task/probe/probe.h @@ -24,6 +24,16 @@ #include "project/item/footage/footage.h" #include "task/task.h" +/** + * @brief The ProbeTask class + * + * A background task for probing a certain Footage file for its metadata and determining if we have a viable decoder + * for it. + * + * Currently this function just calls olive::ProbeMedia() which will call Footage::Clear(), clearing the Footage of + * any previous metadata before passing it through the available decoders until it finds one that can parse it. + * The ProbeTask mostly functions as a background/multithreaded wrapper for this functionality. + */ class ProbeTask : public Task { Q_OBJECT diff --git a/app/ui/icons/icons.cpp b/app/ui/icons/icons.cpp index e980430b9..9bb126603 100644 --- a/app/ui/icons/icons.cpp +++ b/app/ui/icons/icons.cpp @@ -62,6 +62,7 @@ QIcon olive::icon::ZoomOut; QIcon olive::icon::Record; QIcon olive::icon::Add; QIcon olive::icon::Error; +QIcon olive::icon::DirUp; void olive::icon::LoadAll() { @@ -101,6 +102,7 @@ void olive::icon::LoadAll() Record = Create("record"); Add = Create("add-button"); Error = Create("error"); + DirUp = Create("dirup"); } QIcon olive::icon::Create(const QString &name) diff --git a/app/ui/icons/icons.h b/app/ui/icons/icons.h index a73d17768..b8aedb780 100644 --- a/app/ui/icons/icons.h +++ b/app/ui/icons/icons.h @@ -67,6 +67,7 @@ extern QIcon ZoomOut; extern QIcon Record; extern QIcon Add; extern QIcon Error; +extern QIcon DirUp; /** * @brief Create an icon object loaded from file diff --git a/app/widget/projectexplorer/CMakeLists.txt b/app/widget/projectexplorer/CMakeLists.txt index 610e2644b..e999f1d1d 100644 --- a/app/widget/projectexplorer/CMakeLists.txt +++ b/app/widget/projectexplorer/CMakeLists.txt @@ -24,7 +24,13 @@ set(OLIVE_SOURCES widget/projectexplorer/projectexplorerlistview.cpp widget/projectexplorer/projectexplorerlistviewbase.h widget/projectexplorer/projectexplorerlistviewbase.cpp + widget/projectexplorer/projectexplorerlistviewitemdelegate.h + widget/projectexplorer/projectexplorerlistviewitemdelegate.cpp widget/projectexplorer/projectexplorericonview.h widget/projectexplorer/projectexplorericonview.cpp + widget/projectexplorer/projectexplorericonviewitemdelegate.h + widget/projectexplorer/projectexplorericonviewitemdelegate.cpp + widget/projectexplorer/projectexplorernavigation.h + widget/projectexplorer/projectexplorernavigation.cpp PARENT_SCOPE ) diff --git a/app/widget/projectexplorer/projectexplorer.cpp b/app/widget/projectexplorer/projectexplorer.cpp index 955a550ea..599665896 100644 --- a/app/widget/projectexplorer/projectexplorer.cpp +++ b/app/widget/projectexplorer/projectexplorer.cpp @@ -46,8 +46,18 @@ void ProjectExplorer::set_view_type(olive::ProjectViewType type) { view_type_ = type; - // Enum values correspond to the index of the widget in this stacked widget - setCurrentIndex(view_type_); + // Set widget based on view type + switch (view_type_) { + case olive::TreeView: + setCurrentWidget(tree_view_); + break; + case olive::ListView: + setCurrentWidget(list_view_); + break; + case olive::IconView: + setCurrentWidget(icon_view_); + break; + } } void ProjectExplorer::AddView(QAbstractItemView *view) diff --git a/app/widget/projectexplorer/projectexplorericonview.cpp b/app/widget/projectexplorer/projectexplorericonview.cpp index 2adbc6b87..302eec1f7 100644 --- a/app/widget/projectexplorer/projectexplorericonview.cpp +++ b/app/widget/projectexplorer/projectexplorericonview.cpp @@ -24,4 +24,6 @@ ProjectExplorerIconView::ProjectExplorerIconView(QWidget *parent) : ProjectExplorerListViewBase(parent) { setViewMode(QListView::IconMode); + + setItemDelegate(&delegate_); } diff --git a/app/widget/projectexplorer/projectexplorericonview.h b/app/widget/projectexplorer/projectexplorericonview.h index a6af589db..487453a92 100644 --- a/app/widget/projectexplorer/projectexplorericonview.h +++ b/app/widget/projectexplorer/projectexplorericonview.h @@ -22,12 +22,16 @@ #define PROJECTEXPLORERICONVIEW_H #include "projectexplorerlistviewbase.h" +#include "projectexplorericonviewitemdelegate.h" class ProjectExplorerIconView : public ProjectExplorerListViewBase { Q_OBJECT public: ProjectExplorerIconView(QWidget* parent); + +private: + ProjectExplorerIconViewItemDelegate delegate_; }; #endif // PROJECTEXPLORERICONVIEW_H diff --git a/app/widget/projectexplorer/projectexplorericonviewitemdelegate.cpp b/app/widget/projectexplorer/projectexplorericonviewitemdelegate.cpp new file mode 100644 index 000000000..2f5930331 --- /dev/null +++ b/app/widget/projectexplorer/projectexplorericonviewitemdelegate.cpp @@ -0,0 +1,75 @@ +#include "projectexplorericonviewitemdelegate.h" + +#include + +ProjectExplorerIconViewItemDelegate::ProjectExplorerIconViewItemDelegate(QObject *parent) : + QStyledItemDelegate (parent) +{ +} + +QSize ProjectExplorerIconViewItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &) const +{ + Q_UNUSED(option) + + return QSize(256, 256); +} + +void ProjectExplorerIconViewItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QFontMetrics fm = painter->fontMetrics(); + QRect img_rect = option.rect; + + // Draw Text + if (fm.height() < option.rect.height() / 2) { + img_rect.setHeight(img_rect.height()-fm.height()); + + QRect text_rect = option.rect; + text_rect.setTop(text_rect.top() + option.rect.height() - fm.height()); + + QColor text_bgcolor; + QColor text_fgcolor; + + if (option.state & QStyle::State_Selected) { + text_bgcolor = option.palette.highlight().color(); + text_fgcolor = option.palette.highlightedText().color(); + } else { + text_bgcolor = Qt::white; + text_fgcolor = Qt::black; + } + + painter->fillRect(text_rect, text_bgcolor); + painter->setPen(text_fgcolor); + + QString duration_str = index.data(Qt::UserRole).toString(); + int timecode_width = fm.width(duration_str); + int max_name_width = option.rect.width(); + + if (timecode_width < option.rect.width() / 2) { + painter->drawText(text_rect, Qt::AlignBottom | Qt::AlignRight, index.data(Qt::UserRole).toString()); + max_name_width -= timecode_width; + } + + painter->drawText(text_rect, + Qt::AlignBottom | Qt::AlignLeft, + fm.elidedText(index.data(Qt::DisplayRole).toString(), Qt::ElideRight, max_name_width)); + + } + + // Draw image + QIcon ico = index.data(Qt::DecorationRole).value(); + QSize icon_size = ico.actualSize(img_rect.size()); + img_rect = QRect(img_rect.x() + (img_rect.width() / 2 - icon_size.width() / 2), + img_rect.y() + (img_rect.height() / 2 - icon_size.height() / 2), + icon_size.width(), + icon_size.height()); + painter->drawPixmap(img_rect, ico.pixmap(icon_size)); + + if (option.state & QStyle::State_Selected) { + QColor highlight_color = option.palette.highlight().color(); + highlight_color.setAlphaF(0.5); + + painter->setCompositionMode(QPainter::CompositionMode_SourceAtop); + painter->fillRect(img_rect, highlight_color); + } +} + diff --git a/app/widget/projectexplorer/projectexplorericonviewitemdelegate.h b/app/widget/projectexplorer/projectexplorericonviewitemdelegate.h new file mode 100644 index 000000000..fa5066843 --- /dev/null +++ b/app/widget/projectexplorer/projectexplorericonviewitemdelegate.h @@ -0,0 +1,14 @@ +#ifndef PROJECTEXPLORERICONVIEWITEMDELEGATE_H +#define PROJECTEXPLORERICONVIEWITEMDELEGATE_H + +#include + +class ProjectExplorerIconViewItemDelegate : public QStyledItemDelegate { +public: + ProjectExplorerIconViewItemDelegate(QObject *parent = nullptr); + + virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; + virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; +}; + +#endif // PROJECTEXPLORERICONVIEWITEMDELEGATE_H diff --git a/app/widget/projectexplorer/projectexplorerlistview.cpp b/app/widget/projectexplorer/projectexplorerlistview.cpp index c8d90fe92..02672cd27 100644 --- a/app/widget/projectexplorer/projectexplorerlistview.cpp +++ b/app/widget/projectexplorer/projectexplorerlistview.cpp @@ -24,4 +24,6 @@ ProjectExplorerListView::ProjectExplorerListView(QWidget *parent) : ProjectExplorerListViewBase(parent) { setViewMode(QListView::ListMode); + + setItemDelegate(&delegate_); } diff --git a/app/widget/projectexplorer/projectexplorerlistview.h b/app/widget/projectexplorer/projectexplorerlistview.h index ba428f1c1..77962118c 100644 --- a/app/widget/projectexplorer/projectexplorerlistview.h +++ b/app/widget/projectexplorer/projectexplorerlistview.h @@ -22,12 +22,16 @@ #define PROJECTEXPLORERLISTVIEW_H #include "projectexplorerlistviewbase.h" +#include "projectexplorerlistviewitemdelegate.h" class ProjectExplorerListView : public ProjectExplorerListViewBase { Q_OBJECT public: ProjectExplorerListView(QWidget* parent); + +private: + ProjectExplorerListViewItemDelegate delegate_; }; #endif // PROJECTEXPLORERLISTVIEW_H diff --git a/app/widget/projectexplorer/projectexplorerlistviewitemdelegate.cpp b/app/widget/projectexplorer/projectexplorerlistviewitemdelegate.cpp new file mode 100644 index 000000000..2332b9ec4 --- /dev/null +++ b/app/widget/projectexplorer/projectexplorerlistviewitemdelegate.cpp @@ -0,0 +1,59 @@ +#include "projectexplorerlistviewitemdelegate.h" + +#include + +ProjectExplorerListViewItemDelegate::ProjectExplorerListViewItemDelegate(QObject *parent) : + QStyledItemDelegate(parent) +{ + +} + +QSize ProjectExplorerListViewItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &) const +{ + return QSize(option.decorationSize.height(), option.decorationSize.height()); +} + +void ProjectExplorerListViewItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QFontMetrics fm = painter->fontMetrics(); + QRect img_rect = option.rect; + + if (option.state & QStyle::State_Selected) { + painter->fillRect(option.rect, option.palette.highlight()); + } + + img_rect.setWidth(qMin(img_rect.width(), img_rect.height())); + + QIcon ico = index.data(Qt::DecorationRole).value(); + QSize icon_size = ico.actualSize(img_rect.size()); + img_rect = QRect(img_rect.x() + (img_rect.width() / 2 - icon_size.width() / 2), + img_rect.y() + (img_rect.height() / 2 - icon_size.height() / 2), + icon_size.width(), + icon_size.height()); + painter->drawPixmap(img_rect, ico.pixmap(icon_size)); + + QRect text_rect = option.rect; + text_rect.setLeft(text_rect.left() + option.rect.height()); + + int maximum_line_count = qMax(1, option.rect.height() / fm.height() - 1); + QString text; + if (maximum_line_count == 1) { + text = index.data(Qt::DisplayRole).toString(); + } else { + text = index.data(Qt::ToolTipRole).toString(); + if (text.isEmpty()) { + text = index.data(Qt::DisplayRole).toString(); + } else { + QStringList strings = text.split("\n"); + while (strings.size() > maximum_line_count) { + strings.removeLast(); + } + text = strings.join("\n"); + } + } + + painter->setPen(option.state & QStyle::State_Selected ? + option.palette.highlightedText().color() : option.palette.text().color()); + + painter->drawText(text_rect, Qt::AlignLeft | Qt::AlignVCenter, text); +} diff --git a/app/widget/projectexplorer/projectexplorerlistviewitemdelegate.h b/app/widget/projectexplorer/projectexplorerlistviewitemdelegate.h new file mode 100644 index 000000000..e8ca26092 --- /dev/null +++ b/app/widget/projectexplorer/projectexplorerlistviewitemdelegate.h @@ -0,0 +1,15 @@ +#ifndef PROJECTEXPLORERLISTVIEWITEMDELEGATE_H +#define PROJECTEXPLORERLISTVIEWITEMDELEGATE_H + +#include + +class ProjectExplorerListViewItemDelegate : public QStyledItemDelegate +{ +public: + ProjectExplorerListViewItemDelegate(QObject *parent = nullptr); + + virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; + virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; +}; + +#endif // PROJECTEXPLORERLISTVIEWITEMDELEGATE_H diff --git a/app/widget/projectexplorer/projectexplorernavigation.cpp b/app/widget/projectexplorer/projectexplorernavigation.cpp new file mode 100644 index 000000000..9eace5279 --- /dev/null +++ b/app/widget/projectexplorer/projectexplorernavigation.cpp @@ -0,0 +1,15 @@ +#include "projectexplorernavigation.h" + +#include + +#include "ui/icons/icons.h" + +ProjectExplorerNavigation::ProjectExplorerNavigation(QWidget *parent) : + QWidget(parent) +{ + QHBoxLayout* layout = new QHBoxLayout(this); + + dir_up_btn_ = new QPushButton(this); + dir_up_btn_->setIcon(olive::icon::DirUp); + layout->addWidget(dir_up_btn_); +} diff --git a/app/widget/projectexplorer/projectexplorernavigation.h b/app/widget/projectexplorer/projectexplorernavigation.h new file mode 100644 index 000000000..9b7de7457 --- /dev/null +++ b/app/widget/projectexplorer/projectexplorernavigation.h @@ -0,0 +1,16 @@ +#ifndef PROJECTEXPLORERLISTVIEWTOOLBAR_H +#define PROJECTEXPLORERLISTVIEWTOOLBAR_H + +#include +#include + +class ProjectExplorerNavigation : public QWidget +{ +public: + ProjectExplorerNavigation(QWidget* parent); + +private: + QPushButton* dir_up_btn_; +}; + +#endif // PROJECTEXPLORERLISTVIEWTOOLBAR_H