/*** Olive - Non-Linear Video Editor Copyright (C) 2019 Olive Team 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 3 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, see . ***/ #include "core.h" #include #include #include #include #include #include #include #include "dialog/sequence/sequence.h" #include "panel/panelmanager.h" #include "panel/project/project.h" #include "project/item/footage/footage.h" #include "project/item/sequence/sequence.h" #include "render/colorservice.h" #include "task/import/import.h" #include "task/taskmanager.h" #include "ui/style/style.h" #include "undo/undostack.h" #include "widget/menu/menushared.h" #include "widget/taskview/taskviewitem.h" Core olive::core; Core::Core() : main_window_(nullptr), tool_(olive::tool::kPointer), snapping_(true) { } void Core::Start() { // // Parse command line arguments // QCoreApplication* app = QCoreApplication::instance(); QCommandLineParser parser; parser.addHelpOption(); parser.addVersionOption(); // Project from command line option // FIXME: What's the correct way to make a visually "optional" positional argument, or is manually adding square // brackets like this correct? parser.addPositionalArgument("[project]", tr("Project to open on startup")); // Create fullscreen option QCommandLineOption fullscreen_option({"f", "fullscreen"}, tr("Start in full screen mode")); parser.addOption(fullscreen_option); // Parse options parser.process(*app); QStringList args = parser.positionalArguments(); // Detect project to load on startup if (!args.isEmpty()) { startup_project_ = args.first(); } // Declare custom types for Qt signal/slot syste DeclareTypesForQt(); // // Start GUI (FIXME CLI mode) // StartGUI(parser.isSet(fullscreen_option)); // Create new project on startup // FIXME: Load project from startup_project_ instead if not empty AddOpenProject(std::make_shared()); } void Core::Stop() { delete main_window_; } olive::MainWindow *Core::main_window() { return main_window_; } 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(std::make_shared(model, parent, urls)); } const olive::tool::Tool &Core::tool() { return tool_; } const bool &Core::snapping() { return snapping_; } 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; emit ToolChanged(tool_); } void Core::SetSnapping(const bool &b) { snapping_ = b; emit SnappingChanged(snapping_); } void Core::DialogImportShow() { // Open dialog for user to select files QStringList files = QFileDialog::getOpenFileNames(main_window_, tr("Import footage...")); // Check if the user actually selected files to import if (!files.isEmpty()) { // Locate the most recently focused Project panel (assume that's the panel the user wants to import into) ProjectPanel* active_project_panel = olive::panel_focus_manager->MostRecentlyFocused(); Project* active_project; if (active_project_panel == nullptr // Check that we found a Project panel || (active_project = active_project_panel->project()) == nullptr) { // and that we could find an active Project QMessageBox::critical(main_window_, tr("Failed to import footage"), tr("Failed to find active Project panel")); return; } // Get the selected folder in this panel Folder* folder = active_project_panel->GetSelectedFolder(); ImportFiles(files, active_project_panel->model(), folder); } } void Core::CreateNewFolder() { // Locate the most recently focused Project panel (assume that's the panel the user wants to import into) ProjectPanel* active_project_panel = olive::panel_focus_manager->MostRecentlyFocused(); Project* active_project; if (active_project_panel == nullptr // Check that we found a Project panel || (active_project = active_project_panel->project()) == nullptr) { // and that we could find an active Project QMessageBox::critical(main_window_, tr("Failed to create new folder"), tr("Failed to find active Project panel")); return; } // Get the selected folder in this panel Folder* folder = active_project_panel->GetSelectedFolder(); // Create new folder ItemPtr new_folder = std::make_shared(); // Set a default name new_folder->set_name(tr("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.get()); } // FIXME: Test code #include "node/blend/alphaover/alphaover.h" #include "node/color/opacity/opacity.h" #include "node/output/timeline/timeline.h" #include "node/output/track/track.h" #include "node/output/viewer/viewer.h" #include "node/processor/renderer/renderer.h" #include "panel/panelmanager.h" #include "panel/node/node.h" #include "panel/viewer/viewer.h" // End test code void Core::CreateNewSequence() { // Locate the most recently focused Project panel (assume that's the panel the user wants to import into) ProjectPanel* active_project_panel = olive::panel_focus_manager->MostRecentlyFocused(); Project* active_project; if (active_project_panel == nullptr // Check that we found a Project panel || (active_project = active_project_panel->project()) == nullptr) { // and that we could find an active Project QMessageBox::critical(main_window_, tr("Failed to create new sequence"), tr("Failed to find active Project panel")); return; } // Get the selected folder in this panel Folder* folder = active_project_panel->GetSelectedFolder(); // Create new sequence SequencePtr new_sequence = std::make_shared(); // Set all defaults for the sequence new_sequence->SetDefaultParameters(); // Get default name for this sequence (in the format "Sequence N", the first that doesn't exist) int sequence_number = 1; QString sequence_name; do { sequence_name = tr("Sequence %1").arg(sequence_number); sequence_number++; } while (active_project->root()->ChildExistsWithName(sequence_name)); new_sequence->set_name(sequence_name); SequenceDialog sd(new_sequence.get(), SequenceDialog::kNew, main_window_); // Make sure SequenceDialog doesn't make an undo command for editing the sequence, since we make an undo command for // adding it later on sd.SetUndoable(false); if (sd.exec() == QDialog::Accepted) { // Create an undoable command ProjectViewModel::AddItemCommand* aic = new ProjectViewModel::AddItemCommand(active_project_panel->model(), folder, new_sequence); TimelineOutput* tb = new TimelineOutput(); tb->SetTimebase(new_sequence->video_time_base()); new_sequence->AddNode(tb); RendererProcessor* rp = new RendererProcessor(); // Set renderer's parameters based on sequence's parameters rp->SetParameters(new_sequence->video_width(), new_sequence->video_height(), olive::PIX_FMT_RGBA16F, // FIXME: Make this configurable olive::RenderMode::kOffline, 2); // Set the "cache name" only here to aid the cache ID's uniqueness rp->SetCacheName(new_sequence->name()); rp->SetTimebase(new_sequence->video_time_base()); new_sequence->AddNode(rp); ViewerOutput* vo = new ViewerOutput(); vo->SetTimebase(new_sequence->video_time_base()); new_sequence->AddNode(vo); TrackOutput* to = new TrackOutput(); new_sequence->AddNode(to); // Connect track to renderer NodeParam::ConnectEdge(to->texture_output(), rp->texture_input()); // Connect renderer to viewer NodeParam::ConnectEdge(rp->texture_output(), vo->texture_input()); // Connect track to timeline NodeParam::ConnectEdge(to->track_output(), tb->track_input()); // Connect timeline end point to renderer NodeParam::ConnectEdge(tb->length_output(), rp->length_input()); // FIXME: Test code AlphaOverBlend* blend = new AlphaOverBlend(); new_sequence->AddNode(blend); OpacityNode* opac = new OpacityNode(); new_sequence->AddNode(opac); // End test code vo->AttachViewer(olive::panel_focus_manager->MostRecentlyFocused()); tb->AttachTimeline(olive::panel_focus_manager->MostRecentlyFocused()); olive::panel_focus_manager->MostRecentlyFocused()->SetGraph(new_sequence.get()); olive::undo_stack.push(aic); } } void Core::AddOpenProject(ProjectPtr p) { open_projects_.append(p); emit ProjectOpened(p.get()); } void Core::DeclareTypesForQt() { qRegisterMetaType("Task::Status"); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); } void Core::StartGUI(bool full_screen) { // Set UI style olive::style::AppSetDefault(); // Set up shared menus olive::menu_shared.Initialize(); // Since we're starting GUI mode, create a PanelFocusManager (auto-deletes with QObject) olive::panel_focus_manager = new PanelManager(this); // Connect the PanelFocusManager to the application's focus change signal connect(qApp, SIGNAL(focusChanged(QWidget*, QWidget*)), olive::panel_focus_manager, SLOT(FocusChanged(QWidget*, QWidget*))); // Create main window and open it main_window_ = new olive::MainWindow(); if (full_screen) { main_window_->showFullScreen(); } else { main_window_->showMaximized(); } // When a new project is opened, update the mainwindow connect(this, SIGNAL(ProjectOpened(Project*)), main_window_, SLOT(ProjectOpen(Project*))); // Initialize color service ColorService::Init(); } Project *Core::GetActiveProject() { // Locate the most recently focused Project panel (assume that's the panel the user wants to import into) ProjectPanel* active_project_panel = olive::panel_focus_manager->MostRecentlyFocused(); // If we couldn't find one, return nullptr if (active_project_panel == nullptr) { return nullptr; } // Otherwise, return the project panel's project (which may be nullptr but in most cases shouldn't be) return active_project_panel->project(); }