more work on project management

This commit is contained in:
itsmattkc
2019-07-06 09:46:55 -05:00
parent bc631c495d
commit a6971b3fde
15 changed files with 290 additions and 114 deletions
+37 -4
View File
@@ -26,6 +26,7 @@
#include <QFileDialog>
#include <QFileInfo>
#include <QMessageBox>
#include <QHBoxLayout>
#include "panel/panelfocusmanager.h"
#include "panel/project/project.h"
@@ -34,7 +35,9 @@
#include "task/taskmanager.h"
#include "ui/icons/icons.h"
#include "ui/style/style.h"
#include "undo/undostack.h"
#include "widget/menu/menushared.h"
#include "widget/taskview/taskviewitem.h"
Core olive::core;
@@ -100,14 +103,14 @@ olive::MainWindow *Core::main_window()
return main_window_;
}
void Core::ImportFiles(const QStringList &urls, Folder* parent)
void Core::ImportFiles(const QStringList &urls, ProjectViewModel* model, Folder* parent)
{
if (urls.isEmpty()) {
QMessageBox::critical(main_window_, tr("Import error"), tr("Nothing to import"));
return;
}
olive::task_manager.AddTask(new ImportTask(parent, urls));
olive::task_manager.AddTask(new ImportTask(model, parent, urls));
}
const olive::tool::Tool &Core::tool()
@@ -115,6 +118,26 @@ const olive::tool::Tool &Core::tool()
return tool_;
}
void Core::StartModalTask(Task *t)
{
QDialog dialog(main_window_);
QHBoxLayout* layout = new QHBoxLayout(&dialog);
layout->setMargin(0);
TaskViewItem* task_view = new TaskViewItem(&dialog);
task_view->SetTask(t);
layout->addWidget(task_view);
connect(t, SIGNAL(Finished()), &dialog, SLOT(accept()));
// FIXME: Risk of task finishing before dialog execs?
if (t->Start()) {
dialog.exec();
}
}
void Core::SetTool(const olive::tool::Tool &tool)
{
tool_ = tool;
@@ -144,7 +167,7 @@ void Core::StartImportFootage()
// Get the selected folder in this panel
Folder* folder = active_project_panel->GetSelectedFolder();
ImportFiles(files, folder);
ImportFiles(files, active_project_panel->model(), folder);
}
}
@@ -163,11 +186,21 @@ void Core::CreateNewFolder()
// Get the selected folder in this panel
Folder* folder = active_project_panel->GetSelectedFolder();
// Create new folder
Folder* new_folder = new Folder();
// Set a default name
new_folder->set_name(tr("New Folder"));
active_project_panel->model()->AddChild(folder, new_folder);
// Create an undoable command
ProjectViewModel::AddItemCommand* aic = new ProjectViewModel::AddItemCommand(active_project_panel->model(),
folder,
new_folder);
olive::undo_stack.push(aic);
// Trigger an automatic rename so users can enter the folder name
active_project_panel->Edit(new_folder);
}
void Core::AddOpenProject(ProjectPtr p)
+15 -1
View File
@@ -24,7 +24,9 @@
#include <QList>
#include "project/project.h"
#include "project/projectviewmodel.h"
#include "window/mainwindow/mainwindow.h"
#include "task/task.h"
#include "tool/tool.h"
/**
@@ -73,15 +75,27 @@ public:
/**
* @brief Import a list of files
*
* FIXME: I kind of hate this, it needs a model to update correctly. Is there a way that Items can signal enough to
* make passing references to the model unnecessary?
*
* @param urls
*/
void ImportFiles(const QStringList& urls, Folder *parent);
void ImportFiles(const QStringList& urls, ProjectViewModel *model, Folder *parent);
/**
* @brief Get the currently active tool
*/
const olive::tool::Tool& tool();
/**
* @brief Starts a modal task
*
* This function does NOT take ownership of the Task.
*
* @param t
*/
void StartModalTask(Task* t);
public slots:
/**
* @brief Set the current application-wide tool
-4
View File
@@ -186,8 +186,6 @@ bool FFmpegDecoder::Probe(Footage *f)
str = video_stream;
qDebug() << "Stream V" << i << "Len" << (rational(avstream_->duration) * rational(avstream_->time_base)).ToDouble();
} else if (avstream_->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
// Create an audio stream object
@@ -199,8 +197,6 @@ bool FFmpegDecoder::Probe(Footage *f)
str = audio_stream;
qDebug() << "Stream A" << i << "Len" << (rational(avstream_->duration) * rational(avstream_->time_base)).ToDouble();
} else {
// This is data we can't utilize at the moment, but we make a Stream object anyway to keep parity with the file
+6 -1
View File
@@ -32,7 +32,7 @@ ProjectPanel::ProjectPanel(QWidget *parent) :
QWidget* central_widget = new QWidget(this);
QVBoxLayout* layout = new QVBoxLayout(central_widget);
layout->setMargin(0);
layout->setSpacing(0);
//layout->setSpacing(0);
setWidget(central_widget);
// Set up project toolbar
@@ -84,6 +84,11 @@ ProjectViewModel *ProjectPanel::model()
return explorer_->model();
}
void ProjectPanel::Edit(Item* item)
{
explorer_->Edit(item);
}
void ProjectPanel::changeEvent(QEvent *e)
{
if (e->type() == QEvent::LanguageChange) {
+3
View File
@@ -40,6 +40,9 @@ public:
ProjectViewModel* model();
public slots:
void Edit(Item *item);
protected:
virtual void changeEvent(QEvent* e) override;
-18
View File
@@ -23,24 +23,6 @@
Project::Project()
{
name_ = tr("(untitled)");
// FIXME: Test code
Folder* f1 = new Folder();
f1->set_name("F1");
f1->set_parent(&root_);
Folder* f2 = new Folder();
f2->set_name("F2");
f2->set_parent(f1);
Folder* f3 = new Folder();
f3->set_name("F3");
f3->set_parent(&root_);
Folder* f4 = new Folder();
f4->set_name("F4");
f4->set_parent(f1);
Folder* f5 = new Folder();
f5->set_name("F5");
f5->set_parent(f3);
// End Test code
}
Folder *Project::root()
+55 -7
View File
@@ -370,7 +370,7 @@ bool ProjectViewModel::dropMimeData(const QMimeData *data, Qt::DropAction action
}
// Trigger an import
olive::core.ImportFiles(urls, static_cast<Folder*>(drop_item));
olive::core.ImportFiles(urls, this, static_cast<Folder*>(drop_item));
}
return false;
@@ -391,6 +391,32 @@ void ProjectViewModel::AddChild(Item *parent, Item *child)
endInsertRows();
}
void ProjectViewModel::RemoveChild(Item *parent, Item *child)
{
QModelIndex parent_index;
if (parent != project_->root()) {
parent_index = CreateIndexFromItem(parent);
}
int child_row = IndexOfChild(child);
beginRemoveRows(parent_index, child_row, child_row);
parent->remove_child(child);
endRemoveRows();
}
void ProjectViewModel::RenameChild(Item *item, const QString &name)
{
item->set_name(name);
QModelIndex index = CreateIndexFromItem(item, columns_.indexOf(kName));
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
}
int ProjectViewModel::IndexOfChild(Item *item) const
{
// Find parent's index within its own parent
@@ -498,19 +524,41 @@ ProjectViewModel::RenameItemCommand::RenameItemCommand(ProjectViewModel* model,
void ProjectViewModel::RenameItemCommand::redo()
{
set_name(new_name_);
model_->RenameChild(item_, new_name_);
}
void ProjectViewModel::RenameItemCommand::undo()
{
set_name(old_name_);
model_->RenameChild(item_, old_name_);
}
void ProjectViewModel::RenameItemCommand::set_name(const QString &n)
ProjectViewModel::AddItemCommand::AddItemCommand(ProjectViewModel* model, Item* folder, Item* child, QUndoCommand* parent) :
QUndoCommand(parent),
model_(model),
parent_(folder),
child_(child),
done_(false)
{
item_->set_name(n);
}
QModelIndex index = model_->CreateIndexFromItem(item_, model_->columns_.indexOf(kName));
ProjectViewModel::AddItemCommand::~AddItemCommand()
{
// FIXME Use smart pointers. I hate having to do this.
if (!done_) {
delete child_;
}
}
emit model_->dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
void ProjectViewModel::AddItemCommand::redo()
{
model_->AddChild(parent_, child_);
done_ = true;
}
void ProjectViewModel::AddItemCommand::undo()
{
model_->RemoveChild(parent_, child_);
done_ = false;
}
+64 -44
View File
@@ -94,6 +94,70 @@ public:
/** Other model functions */
void AddChild(Item* parent, Item* child);
void RemoveChild(Item* parent, Item* child);
void RenameChild(Item* item, const QString& name);
/**
* @brief Convenience function for creating QModelIndexes from an Item object
*/
QModelIndex CreateIndexFromItem(Item* item, int column = 0);
/**
* @brief A QUndoCommand for moving an item from one folder to another folder
*/
class MoveItemCommand : public QUndoCommand {
public:
MoveItemCommand(ProjectViewModel* model, Item* item, Folder* destination, QUndoCommand* parent = nullptr);
virtual void redo() override;
virtual void undo() override;
private:
ProjectViewModel* model_;
Item* item_;
Folder* source_;
Folder* destination_;
};
/**
* @brief A QUndoCommand for renaming an item
*/
class RenameItemCommand : public QUndoCommand {
public:
RenameItemCommand(ProjectViewModel* model, Item* item, const QString& name, QUndoCommand* parent = nullptr);
virtual void redo() override;
virtual void undo() override;
private:
ProjectViewModel* model_;
Item* item_;
QString old_name_;
QString new_name_;
};
/**
* @brief A QUndoCommand for adding an item
*/
class AddItemCommand : public QUndoCommand {
public:
AddItemCommand(ProjectViewModel* model, Item* folder, Item* child, QUndoCommand* parent = nullptr);
virtual ~AddItemCommand() override;
virtual void redo() override;
virtual void undo() override;
private:
ProjectViewModel* model_;
Item* parent_;
Item* child_;
bool done_;
};
private:
/**
* @brief Retrieve the index of `item` in its parent
@@ -144,53 +208,9 @@ private:
*/
void MoveItemInternal(Item* item, Item* destination);
/**
* @brief Convenience function for creating QModelIndexes from an Item object
*/
QModelIndex CreateIndexFromItem(Item* item, int column = 0);
Project* project_;
QVector<ColumnType> columns_;
/**
* @brief A QUndoCommand for moving an item from one folder to another folder
*/
class MoveItemCommand : public QUndoCommand {
public:
MoveItemCommand(ProjectViewModel* model, Item* item, Folder* destination, QUndoCommand* parent = nullptr);
virtual void redo() override;
virtual void undo() override;
private:
ProjectViewModel* model_;
Item* item_;
Folder* source_;
Folder* destination_;
};
/**
* @brief A QUndoCommand for renaming an item
*/
class RenameItemCommand : public QUndoCommand {
public:
RenameItemCommand(ProjectViewModel* model, Item* item, const QString& name, QUndoCommand* parent = nullptr);
virtual void redo() override;
virtual void undo() override;
private:
void set_name(const QString& n);
ProjectViewModel* model_;
Item* item_;
QString old_name_;
QString new_name_;
};
};
#endif // VIEWMODEL_H
+30 -20
View File
@@ -1,3 +1,4 @@
/***
Olive - Non-Linear Video Editor
@@ -33,8 +34,10 @@
#include "project/item/footage/footage.h"
#include "task/probe/probe.h"
#include "task/taskmanager.h"
#include "undo/undostack.h"
ImportTask::ImportTask(Folder *parent, const QStringList &urls) :
ImportTask::ImportTask(ProjectViewModel *model, Folder *parent, const QStringList &urls) :
model_(model),
urls_(urls),
parent_(parent)
{
@@ -43,9 +46,20 @@ ImportTask::ImportTask(Folder *parent, const QStringList &urls) :
bool ImportTask::Action()
{
for (int i=0;i<urls_.size();i++) {
QUndoCommand* command = new QUndoCommand();
const QString& url = urls_.at(i);
Import(urls_, parent_, command);
olive::undo_stack.push(command);
return true;
}
void ImportTask::Import(const QStringList &files, Folder *folder, QUndoCommand *parent_command)
{
for (int i=0;i<files.size();i++) {
const QString& url = files.at(i);
QFileInfo file_info(url);
@@ -65,7 +79,11 @@ bool ImportTask::Action()
f->set_name(file_info.fileName());
parent_->add_child(f);
// Create undoable command that adds the items to the model
new ProjectViewModel::AddItemCommand(model_,
folder,
f,
parent_command);
// Convert QFileInfoList into QStringList
QStringList full_urls;
@@ -76,12 +94,8 @@ bool ImportTask::Action()
}
}
// Start a new import task for this folder too
ImportTask* it = new ImportTask(f, full_urls);
it->moveToThread(qApp->thread());
olive::task_manager.AddTask(it);
// Recursively follow this path
Import(full_urls, f, parent_command);
}
} else {
@@ -95,7 +109,11 @@ bool ImportTask::Action()
f->set_name(file_info.fileName());
f->set_timestamp(file_info.lastModified());
parent_->add_child(f);
// Create undoable command that adds the items to the model
new ProjectViewModel::AddItemCommand(model_,
folder,
f,
parent_command);
// Create ProbeTask to analyze this media
ProbeTask* pt = new ProbeTask(f);
@@ -109,15 +127,7 @@ bool ImportTask::Action()
}
emit ProgressChanged(i * 100 / urls_.size());
emit ProgressChanged(i * 100 / files.size());
}
// FIXME: No good very bad test code to update the ProjectExplorer model/view
// A better solution would be for the Project itself to signal that a new row was being added
ProjectPanel* p = olive::panel_focus_manager->MostRecentlyFocused<ProjectPanel>();
p->set_project(p->project());
// End test code
return true;
}
+5 -1
View File
@@ -21,6 +21,7 @@
#ifndef IMPORT_H
#define IMPORT_H
#include "project/projectviewmodel.h"
#include "project/item/folder/folder.h"
#include "task/task.h"
@@ -36,11 +37,14 @@ class ImportTask : public Task
{
Q_OBJECT
public:
ImportTask(Folder *parent_, const QStringList& urls);
ImportTask(ProjectViewModel* model, Folder *parent, const QStringList& urls);
virtual bool Action() override;
private:
void Import(const QStringList& files, Folder* folder, QUndoCommand* parent_command);
ProjectViewModel* model_;
QStringList urls_;
Folder* parent_;
};
+2
View File
@@ -116,4 +116,6 @@ void Task::ThreadComplete()
} else {
set_status(kError);
}
emit Finished();
}
+5
View File
@@ -176,6 +176,11 @@ signals:
*/
void ProgressChanged(int p);
/**
* @brief Signal emitted when the Task finishes whether it succeeded or failed
*/
void Finished();
private:
/**
* @brief Set the status of this Task (also emits StatusChanged())
+51 -13
View File
@@ -62,6 +62,10 @@ ProjectExplorer::ProjectExplorer(QWidget *parent) :
// Set default icon size
SizeChangedSlot(olive::kProjectIconSizeDefault);
// Set rename timer timeout
rename_timer_.setInterval(500);
connect(&rename_timer_, SIGNAL(timeout()), this, SLOT(RenameTimerSlot()));
}
const olive::ProjectViewType &ProjectExplorer::view_type()
@@ -90,15 +94,25 @@ void ProjectExplorer::set_view_type(olive::ProjectViewType type)
}
}
void ProjectExplorer::Edit(Item *item)
{
CurrentView()->edit(model_.CreateIndexFromItem(item));
}
void ProjectExplorer::AddView(QAbstractItemView *view)
{
view->setModel(&model_);
view->setEditTriggers(QAbstractItemView::NoEditTriggers);
connect(view, SIGNAL(DoubleClickedView(const QModelIndex&)), this, SLOT(DoubleClickViewSlot(const QModelIndex&)));
connect(view, SIGNAL(clicked(const QModelIndex&)), this, SLOT(ItemClickedSlot(const QModelIndex&)));
stacked_widget_->addWidget(view);
}
void ProjectExplorer::BrowseToFolder(const QModelIndex &index)
{
// Make sure any rename timers are stopped
rename_timer_.stop();
// Set appropriate views to this index
icon_view_->setRootIndex(index);
list_view_->setRootIndex(index);
@@ -116,6 +130,30 @@ void ProjectExplorer::BrowseToFolder(const QModelIndex &index)
nav_bar_->set_dir_up_enabled(index.isValid());
}
QAbstractItemView *ProjectExplorer::CurrentView()
{
return static_cast<QAbstractItemView*>(stacked_widget_->currentWidget());
}
void ProjectExplorer::ItemClickedSlot(const QModelIndex &index)
{
if (index.isValid()) {
if (clicked_index_ == index) {
// The item has been clicked more than once, start a timer for renaming
rename_timer_.start();
} else {
// Cache this index for the next click
clicked_index_ = index;
// If the rename timer had started, stop it now
rename_timer_.stop();
}
} else {
// Stop the rename timer
rename_timer_.stop();
}
}
void ProjectExplorer::DoubleClickViewSlot(const QModelIndex &index)
{
if (index.isValid()) {
@@ -160,6 +198,18 @@ void ProjectExplorer::DirUpSlot()
}
}
void ProjectExplorer::RenameTimerSlot()
{
// Start editing this index
CurrentView()->edit(clicked_index_);
// Reset clicked index state
clicked_index_ = QModelIndex();
// Stop rename timer
rename_timer_.stop();
}
Project *ProjectExplorer::project()
{
return model_.project();
@@ -172,20 +222,8 @@ void ProjectExplorer::set_project(Project *p)
QList<Item *> ProjectExplorer::SelectedItems()
{
QModelIndexList index_list;
// Determine which view is active and get its selected indexes
switch (view_type_) {
case olive::TreeView:
index_list = tree_view_->selectionModel()->selectedRows();
break;
case olive::ListView:
index_list = list_view_->selectionModel()->selectedRows();
break;
case olive::IconView:
index_list = icon_view_->selectionModel()->selectedRows();
break;
}
QModelIndexList index_list = CurrentView()->selectionModel()->selectedRows();
// Convert indexes to item objects
QList<Item*> selected_items;
@@ -22,6 +22,7 @@
#define PROJECTEXPLORER_H
#include <QStackedWidget>
#include <QTimer>
#include <QTreeView>
#include "project/project.h"
@@ -77,6 +78,8 @@ public:
public slots:
void set_view_type(olive::ProjectViewType type);
void Edit(Item* item);
signals:
/**
* @brief Emitted when an Item is double clicked
@@ -110,6 +113,11 @@ private:
*/
void BrowseToFolder(const QModelIndex& index);
/**
* @brief Get the currently active QAbstractItemView
*/
QAbstractItemView* CurrentView();
QStackedWidget* stacked_widget_;
ProjectExplorerNavigation* nav_bar_;
@@ -122,12 +130,20 @@ private:
ProjectViewModel model_;
QModelIndex clicked_index_;
QTimer rename_timer_;
private slots:
void ItemClickedSlot(const QModelIndex& index);
void DoubleClickViewSlot(const QModelIndex& index);
void SizeChangedSlot(int s);
void DirUpSlot();
void RenameTimerSlot();
};
#endif // PROJECTEXPLORER_H
@@ -29,7 +29,7 @@ class ProjectExplorerListViewBase : public QListView
public:
ProjectExplorerListViewBase(QWidget* parent);
protected:
protected:
/**
* @brief Double click event override
*