diff --git a/src/docks/recentdock.cpp b/src/docks/recentdock.cpp index 0b887dcc97..3bc5769121 100644 --- a/src/docks/recentdock.cpp +++ b/src/docks/recentdock.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2023 Meltytech, LLC + * Copyright (c) 2012-2024 Meltytech, LLC * * 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 @@ -26,7 +26,7 @@ #include #include -static const int MaxItems = 100; +static const int MaxItems = 200; RecentDock::RecentDock(QWidget *parent) : QDockWidget(parent), @@ -89,6 +89,11 @@ void RecentDock::add(const QString &s) while (m_recent.count() > MaxItems) m_recent.removeLast(); Settings.setRecent(m_recent); + if (filePath.endsWith(".mlt")) { + auto projects = Settings.projects(); + projects.prepend(filePath); + Settings.setProjects(projects); + } } void RecentDock::on_listWidget_activated(const QModelIndex &i) @@ -138,6 +143,11 @@ void RecentDock::on_actionDelete_triggered() m_recent.removeAt(row); Settings.setRecent(m_recent); m_model.removeRow(row); + if (url.endsWith(".mlt")) { + auto ls = Settings.projects(); + if (ls.removeAll(url) > 0) + Settings.setProjects(ls); + } emit deleted(url); } } diff --git a/src/settings.cpp b/src/settings.cpp index 3b460e510d..f15e74b5b5 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -33,6 +33,7 @@ static QScopedPointer instance; static QString appDataForSession; static const int kMaximumTrackHeight = 125; static const QString kRecentKey("recent"); +static const QString kProjectsKey("projects"); ShotcutSettings &ShotcutSettings::singleton() { @@ -179,6 +180,30 @@ void ShotcutSettings::setRecent(const QStringList &ls) m_recent.setValue(kRecentKey, ls); } +QStringList ShotcutSettings::projects() +{ + auto ls = m_recent.value(kProjectsKey).toStringList(); + if (ls.isEmpty()) { + for (auto &r : recent()) { + if (r.endsWith(".mlt")) + ls << r; + } + // Prevent entering this block repeatedly + if (ls.isEmpty()) + ls << QString(); + setProjects(ls); + } + return ls; +} + +void ShotcutSettings::setProjects(const QStringList &ls) +{ + if (ls.isEmpty()) + m_recent.remove(kProjectsKey); + else if (!clearRecent()) + m_recent.setValue(kProjectsKey, ls); +} + QString ShotcutSettings::theme() const { return settings.value("theme", "dark").toString(); diff --git a/src/settings.h b/src/settings.h index ef1285154e..c6a06e67a8 100644 --- a/src/settings.h +++ b/src/settings.h @@ -97,6 +97,8 @@ class ShotcutSettings : public QObject void setSavePath(const QString &); QStringList recent() const; void setRecent(const QStringList &); + QStringList projects(); + void setProjects(const QStringList &); QString theme() const; void setTheme(const QString &); QThread::Priority jobPriority() const; diff --git a/src/widgets/newprojectfolder.cpp b/src/widgets/newprojectfolder.cpp index 34d8c55ded..89d726a0b2 100644 --- a/src/widgets/newprojectfolder.cpp +++ b/src/widgets/newprojectfolder.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2022 Meltytech, LLC + * Copyright (c) 2018-2024 Meltytech, LLC * * 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 @@ -111,8 +111,8 @@ bool NewProjectFolder::event(QEvent *event) void NewProjectFolder::updateRecentProjects() { m_model.clear(); - foreach (QString s, Settings.recent()) { - if (s.endsWith(".mlt")) { + for (auto &s : Settings.projects()) { + if (!s.isEmpty()) { QStandardItem *item = new QStandardItem(Util::baseName(s)); item->setToolTip(QDir::toNativeSeparators(s)); m_model.appendRow(item); @@ -293,3 +293,32 @@ void NewProjectFolder::on_recentListView_doubleClicked(const QModelIndex &index) { on_recentListView_clicked(index); } + +void NewProjectFolder::on_recentListView_customContextMenuRequested(const QPoint &pos) +{ + if (ui->recentListView->currentIndex().isValid()) { + QMenu menu(this); + menu.addAction(ui->actionRecentRemove); + menu.exec(ui->recentListView->mapToGlobal(pos)); + } +} + + +void NewProjectFolder::on_actionRecentRemove_triggered() +{ + if (ui->recentListView->currentIndex().isValid()) { + auto index = ui->recentListView->currentIndex(); + auto data = m_model.itemData(index); + auto projects = Settings.projects(); + auto url = data[Qt::ToolTipRole].toString(); + url = QDir::fromNativeSeparators(url); + if (projects.removeAll(url) > 0) { + m_model.removeRow(index.row()); + Settings.setProjects(projects); + emit deletedProject(url); + } else { + LOG_WARNING() << "Failed to remove project" << url; + } + } +} + diff --git a/src/widgets/newprojectfolder.h b/src/widgets/newprojectfolder.h index 75e9c4faa0..a9274f7905 100644 --- a/src/widgets/newprojectfolder.h +++ b/src/widgets/newprojectfolder.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020 Meltytech, LLC + * Copyright (c) 2018-2024 Meltytech, LLC * * 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 @@ -47,6 +47,9 @@ class NewProjectFolder : public QWidget public slots: void updateRecentProjects(); +signals: + void deletedProject(const QString &); + private slots: void on_projectsFolderButton_clicked(); @@ -66,6 +69,10 @@ private slots: void on_recentListView_doubleClicked(const QModelIndex &index); + void on_recentListView_customContextMenuRequested(const QPoint &pos); + + void on_actionRecentRemove_triggered(); + private: void setColors(); void setProjectFolderButtonText(const QString &text); diff --git a/src/widgets/newprojectfolder.ui b/src/widgets/newprojectfolder.ui index 982b65f8c9..ab2866a689 100644 --- a/src/widgets/newprojectfolder.ui +++ b/src/widgets/newprojectfolder.ui @@ -29,7 +29,7 @@ - QFrame::Box + QFrame::Shape::Box 2 @@ -58,10 +58,10 @@ - Recent Projects + Projects - Qt::AlignCenter + Qt::AlignmentFlag::AlignCenter 4 @@ -85,11 +85,14 @@ + + Qt::ContextMenuPolicy::CustomContextMenu + - QFrame::NoFrame + QFrame::Shape::NoFrame - QAbstractItemView::NoEditTriggers + QAbstractItemView::EditTrigger::NoEditTriggers true @@ -108,7 +111,7 @@ - QFrame::Panel + QFrame::Shape::Panel 2 @@ -140,7 +143,7 @@ New Project - Qt::AlignCenter + Qt::AlignmentFlag::AlignCenter 4 @@ -156,14 +159,14 @@ Projects folder - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - Qt::Vertical + Qt::Orientation::Vertical @@ -232,7 +235,7 @@ a project file with the same name. - Qt::Horizontal + Qt::Orientation::Horizontal @@ -250,7 +253,7 @@ a project file with the same name. Video mode - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter @@ -260,7 +263,7 @@ a project file with the same name. Project name - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter @@ -273,13 +276,13 @@ a project file with the same name. - QFrame::NoFrame + QFrame::Shape::NoFrame - Qt::ScrollBarAlwaysOff + Qt::ScrollBarPolicy::ScrollBarAlwaysOff - QAbstractScrollArea::AdjustToContents + QAbstractScrollArea::SizeAdjustPolicy::AdjustToContents true @@ -289,8 +292,8 @@ a project file with the same name. 0 0 - 146 - 144 + 153 + 220 @@ -312,7 +315,7 @@ a project file with the same name. Automatic means the resolution and frame rate are based on the <b>first</b> file you <b>add</b> to your project. If the first file is not a video clip (for example, image or audio), then it will be 1920x1080p 25 fps. - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop true @@ -332,7 +335,7 @@ a project file with the same name. - Qt::Vertical + Qt::Orientation::Vertical @@ -364,6 +367,14 @@ a project file with the same name. Remove... + + + Remove + + + QAction::MenuRole::NoRole + + projectsFolderButton