right click menu in node view for adding nodes
This commit is contained in:
@@ -43,11 +43,13 @@
|
||||
#include "dialogs/newsequencedialog.h"
|
||||
#include "dialogs/loaddialog.h"
|
||||
#include "dialogs/autocutsilencedialog.h"
|
||||
#include "effects/effectloaders.h"
|
||||
#include "project/loadthread.h"
|
||||
#include "project/savethread.h"
|
||||
#include "timeline/sequence.h"
|
||||
#include "ui/mediaiconservice.h"
|
||||
#include "ui/mainwindow.h"
|
||||
#include "ui/menu.h"
|
||||
#include "ui/updatenotification.h"
|
||||
#include "undo/undostack.h"
|
||||
|
||||
@@ -450,6 +452,40 @@ void OliveGlobal::PasteInternal(Sequence *s, bool insert)
|
||||
}
|
||||
}
|
||||
|
||||
void OliveGlobal::EffectMenuAction(QAction *q)
|
||||
{
|
||||
ComboAction* ca = new ComboAction();
|
||||
|
||||
NodeType node_type = static_cast<NodeType>(q->data().toInt());
|
||||
Node* n = olive::node_library[node_type].get();
|
||||
|
||||
for (int i=0;i<effect_menu_selected_clips.size();i++) {
|
||||
Clip* c = effect_menu_selected_clips.at(i);
|
||||
if (c->type() == n->subtype()) {
|
||||
if (n->type() == EFFECT_TYPE_TRANSITION) {
|
||||
if (c->opening_transition == nullptr) {
|
||||
ca->append(new AddTransitionCommand(c,
|
||||
nullptr,
|
||||
nullptr,
|
||||
node_type,
|
||||
olive::config.default_transition_length));
|
||||
}
|
||||
if (c->closing_transition == nullptr) {
|
||||
ca->append(new AddTransitionCommand(nullptr,
|
||||
c,
|
||||
nullptr,
|
||||
node_type,
|
||||
olive::config.default_transition_length));
|
||||
}
|
||||
} else {
|
||||
ca->append(new AddEffectCommand(c, nullptr, node_type));
|
||||
}
|
||||
}
|
||||
}
|
||||
olive::undo_stack.push(ca);
|
||||
update_ui(true);
|
||||
}
|
||||
|
||||
void OliveGlobal::ImportProject(const QString &fn)
|
||||
{
|
||||
LoadProject(fn, false);
|
||||
@@ -644,6 +680,77 @@ bool OliveGlobal::CheckForActiveSequence(bool show_msg)
|
||||
return true;
|
||||
}
|
||||
|
||||
void OliveGlobal::ShowEffectMenu(EffectType type, olive::TrackType subtype, const QVector<Clip*> selected_clips)
|
||||
{
|
||||
effect_menu_selected_clips = selected_clips;
|
||||
|
||||
olive::effects_loaded.lock();
|
||||
|
||||
Menu effects_menu(olive::MainWindow);
|
||||
effects_menu.setToolTipsVisible(true);
|
||||
|
||||
for (int i=0;i<olive::node_library.size();i++) {
|
||||
|
||||
NodePtr node = olive::node_library.at(i);
|
||||
|
||||
if (node != nullptr && node->type() == type && node->subtype() == subtype) {
|
||||
QAction* action = new QAction(&effects_menu);
|
||||
action->setText(node->name());
|
||||
action->setData(i);
|
||||
if (!node->description().isEmpty()) {
|
||||
action->setToolTip(node->description());
|
||||
}
|
||||
|
||||
QMenu* parent = &effects_menu;
|
||||
if (!node->category().isEmpty()) {
|
||||
bool found = false;
|
||||
for (int j=0;j<effects_menu.actions().size();j++) {
|
||||
QAction* action = effects_menu.actions().at(j);
|
||||
if (action->menu() != nullptr) {
|
||||
if (action->menu()->title() == node->category()) {
|
||||
parent = action->menu();
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
parent = new Menu(&effects_menu);
|
||||
parent->setToolTipsVisible(true);
|
||||
parent->setTitle(node->category());
|
||||
|
||||
bool found = false;
|
||||
for (int i=0;i<effects_menu.actions().size();i++) {
|
||||
QAction* comp_action = effects_menu.actions().at(i);
|
||||
if (comp_action->text() > node->category()) {
|
||||
effects_menu.insertMenu(comp_action, parent);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) effects_menu.addMenu(parent);
|
||||
}
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
for (int i=0;i<parent->actions().size();i++) {
|
||||
QAction* comp_action = parent->actions().at(i);
|
||||
if (comp_action->text() > action->text()) {
|
||||
parent->insertAction(comp_action, action);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) parent->addAction(action);
|
||||
}
|
||||
}
|
||||
|
||||
olive::effects_loaded.unlock();
|
||||
|
||||
connect(&effects_menu, SIGNAL(triggered(QAction*)), this, SLOT(EffectMenuAction(QAction*)));
|
||||
effects_menu.exec(QCursor::pos());
|
||||
}
|
||||
|
||||
void OliveGlobal::undo() {
|
||||
// workaround to prevent crash (and also users should never need to do this)
|
||||
if (!Timeline::IsImporting()) {
|
||||
|
||||
+135
-120
@@ -34,23 +34,23 @@
|
||||
* A resource for various global functions used throughout Olive.
|
||||
*/
|
||||
class OliveGlobal : public QObject {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
/**
|
||||
* @brief OliveGlobal Constructor
|
||||
*
|
||||
* Creates Olive Global object. Also sets some default runtime settings and the application name.
|
||||
*/
|
||||
OliveGlobal();
|
||||
OliveGlobal();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns the file dialog filter used when interfacing with Olive project files.
|
||||
*
|
||||
* @return The file filter string used by QFileDialog to limit the files shown to Olive (*.ove) files.
|
||||
*/
|
||||
const QString& get_project_file_filter();
|
||||
const QString& get_project_file_filter();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Change the current active project filename
|
||||
*
|
||||
* Triggered to change the current active project filename. Call this before calling any internal project
|
||||
@@ -63,18 +63,18 @@ public:
|
||||
* The URL of the project file to work with. Can be an empty string, in which case Olive will treat the project
|
||||
* as an unsaved project.
|
||||
*/
|
||||
void update_project_filename(const QString& s);
|
||||
void update_project_filename(const QString& s);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Check whether an auto-recovery file exists and ask the user if they want to load it.
|
||||
*
|
||||
* Usually called on initialization. Checks if an auto-recovery file exists (meaning the last session of Olive
|
||||
* didn't close correctly). If it finds one, asks the user if they want to load it. If so, loads the auto-recovery
|
||||
* project.
|
||||
*/
|
||||
void check_for_autorecovery_file();
|
||||
void check_for_autorecovery_file();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Get whether the project is currently being rendered or not. Useful for determining whether to treat the
|
||||
* render as online or offline.
|
||||
*
|
||||
@@ -82,9 +82,9 @@ public:
|
||||
*
|
||||
* TRUE if the project is being exported, FALSE if not.
|
||||
*/
|
||||
bool is_exporting();
|
||||
bool is_exporting();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Set the application state depending on if the user is exporting a video
|
||||
*
|
||||
* Some background functions shouldn't run while Olive is exporting a video. This function will disable/enable them
|
||||
@@ -100,9 +100,9 @@ public:
|
||||
*
|
||||
* **TRUE** if Olive is about to export a video. **FALSE** if Olive has finished exporting.
|
||||
*/
|
||||
void set_export_state(bool rendering);
|
||||
void set_export_state(bool rendering);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Set the application's "modified" state
|
||||
*
|
||||
* Primarily controls whether the application prompts the user to save the project upon closing or not. Also
|
||||
@@ -113,9 +113,9 @@ public:
|
||||
*
|
||||
* TRUE if the project has been modified, FALSE if it has not.
|
||||
*/
|
||||
void set_modified(bool modified);
|
||||
void set_modified(bool modified);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Get application's current "modified" state
|
||||
*
|
||||
* Currently just a wrapper around MainWindow::isWindowModified(), but use this instead in case it changes.
|
||||
@@ -125,9 +125,9 @@ public:
|
||||
*
|
||||
* TRUE if the project has been modified since the last save.
|
||||
*/
|
||||
bool is_modified();
|
||||
bool is_modified();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Set a project to load just after launching
|
||||
*
|
||||
* Called by main() if Olive was called with a project file as a running argument. Sets up Olive to load the
|
||||
@@ -137,54 +137,54 @@ public:
|
||||
*
|
||||
* The URL of the project file to load.
|
||||
*/
|
||||
void load_project_on_launch(const QString& s);
|
||||
void load_project_on_launch(const QString& s);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Retrieves the URL of the config file containing the autorecovery projects
|
||||
* @return The URL as a string
|
||||
*/
|
||||
QString get_recent_project_list_file();
|
||||
QString get_recent_project_list_file();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief (Re)load translation file from olive::config
|
||||
*/
|
||||
void load_translation_from_config();
|
||||
void load_translation_from_config();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Set native UI styling on a given widget
|
||||
*
|
||||
* @param w
|
||||
*
|
||||
* The widget to set styling on.
|
||||
*/
|
||||
static void SetNativeStyling(QWidget* w);
|
||||
static void SetNativeStyling(QWidget* w);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Adds a project URL to the recent projects list
|
||||
*
|
||||
* @param url
|
||||
*
|
||||
* The project URL to add
|
||||
*/
|
||||
void add_recent_project(const QString& url);
|
||||
void add_recent_project(const QString& url);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Load recent projects from file
|
||||
*
|
||||
* Should be called on application startup.
|
||||
*/
|
||||
void load_recent_projects();
|
||||
void load_recent_projects();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Total count of recent projects
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* Number of recent projects in the list
|
||||
*/
|
||||
int recent_project_count();
|
||||
int recent_project_count();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Get the recent project at a given index
|
||||
*
|
||||
* @param index
|
||||
@@ -193,18 +193,18 @@ public:
|
||||
*
|
||||
* The recent project at index
|
||||
*/
|
||||
const QString& recent_project(int index);
|
||||
const QString& recent_project(int index);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Retrieves the filename of the autorecovery file to save to during this session
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* A URL pointing to the autorecovery file
|
||||
*/
|
||||
const QString& get_autorecovery_filename();
|
||||
const QString& get_autorecovery_filename();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Returns whether a Sequence is currently active or not, and optionally displays a messagebox if not
|
||||
*
|
||||
* Checks whether a Sequence is active and can display a messagebox if not to inform users to make one active in
|
||||
@@ -214,29 +214,32 @@ public:
|
||||
*
|
||||
* TRUE if there is an active Sequence, FALSE if not.
|
||||
*/
|
||||
bool CheckForActiveSequence(bool show_msg = true);
|
||||
bool CheckForActiveSequence(bool show_msg = true);
|
||||
|
||||
|
||||
void ShowEffectMenu(EffectType type, olive::TrackType subtype, const QVector<Clip*> selected_clips);
|
||||
|
||||
public slots:
|
||||
/**
|
||||
/**
|
||||
* @brief Undo user's last action
|
||||
*/
|
||||
void undo();
|
||||
void undo();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Redo user's last action
|
||||
*/
|
||||
void redo();
|
||||
void redo();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Paste contents of clipboard
|
||||
*
|
||||
* Pastes contents of clipboard. Seeing as several types of data can be copied into the clipboard, this
|
||||
* function will automatically determine what type of data is in the clipboard and paste it in the correct
|
||||
* location (e.g. clip data will go to the Timeline, effect data will go to Effect Controls).
|
||||
*/
|
||||
void paste();
|
||||
void paste();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Paste contents of clipboard, making space for it when possible
|
||||
*
|
||||
* Pastes contents of clipboard (same as paste()). If the clipboard contains clip data, the clips are cut at the
|
||||
@@ -244,25 +247,25 @@ public slots:
|
||||
* semi-non-destructive as a result (as opposed to paste() overwriting clips). If the clipboard contains effect
|
||||
* data, the functionality is identical to paste().
|
||||
*/
|
||||
void paste_insert();
|
||||
void paste_insert();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Create new project.
|
||||
*
|
||||
* Confirms whether the current project can be closed, and if so, clears all current project data and resets
|
||||
* program state. Standard `File > New` behavior.
|
||||
*/
|
||||
void new_project();
|
||||
void new_project();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Open a project from file.
|
||||
*
|
||||
* Confirms whether the current project can be closed, and if so, shows an open file dialog to allow the user to
|
||||
* select a project file and then triggers a project load with it.
|
||||
*/
|
||||
void OpenProject();
|
||||
void OpenProject();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Import project from file
|
||||
*
|
||||
* Imports an Olive project into the current project, effectively merging them.
|
||||
@@ -271,9 +274,9 @@ public slots:
|
||||
*
|
||||
* The filename of the project to import.
|
||||
*/
|
||||
void ImportProject(const QString& fn);
|
||||
void ImportProject(const QString& fn);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Open recent project from list
|
||||
*
|
||||
* Triggers a project load from the internal recent projects list.
|
||||
@@ -282,9 +285,9 @@ public slots:
|
||||
*
|
||||
* Index in the list of the project fille to load
|
||||
*/
|
||||
void open_recent(int index);
|
||||
void open_recent(int index);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Shows a save file dialog and saves the project as the resulting filename
|
||||
*
|
||||
* Shows a save file dialog for the user to save their current project as a different filename from the current
|
||||
@@ -294,9 +297,9 @@ public slots:
|
||||
* if a user is closing an unsaved project, clicks "Yes" to save, we know if they actually saved or not and won't
|
||||
* continue closing the project if they didn't.
|
||||
*/
|
||||
bool save_project_as();
|
||||
bool save_project_as();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Saves the current project to file
|
||||
*
|
||||
* If the project has been saved already, this function will overwrite the project file with the current project
|
||||
@@ -306,9 +309,9 @@ public slots:
|
||||
* value of save_project_as(). Useful if the user closing an unsaved project, clicks "Yes" to save, we know if they
|
||||
* actually saved or not and won't continue closing the project if they didn't.
|
||||
*/
|
||||
bool save_project();
|
||||
bool save_project();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Determine whether the current project can be closed.
|
||||
*
|
||||
* Queried any time the current project is going to be closed (e.g. starting a new project, loading a project,
|
||||
@@ -320,93 +323,93 @@ public slots:
|
||||
* returns **TRUE**. If it does and the user clicks YES, this returns the result of save_project(). If the user
|
||||
* clicks NO, this returns **TRUE**. If the user clicks CANCEL, this returns **FALSE**.
|
||||
*/
|
||||
bool can_close_project();
|
||||
bool can_close_project();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Opens the NewSequenceDialog to create a new Sequence
|
||||
*/
|
||||
void open_new_sequence_dialog();
|
||||
void open_new_sequence_dialog();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Open a file dialog for importing files into the project
|
||||
*/
|
||||
void open_import_dialog();
|
||||
void open_import_dialog();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Open the Export dialog to trigger an export of the current sequence.
|
||||
*/
|
||||
void open_export_dialog();
|
||||
void open_export_dialog();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Open the About Olive dialog.
|
||||
*/
|
||||
void open_about_dialog();
|
||||
void open_about_dialog();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Open the Debug Log window.
|
||||
*/
|
||||
void open_debug_log();
|
||||
void open_debug_log();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Open the Speed/Duration dialog.
|
||||
*/
|
||||
void open_speed_dialog();
|
||||
void open_speed_dialog();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Open the auto-cut silence dialog.
|
||||
*/
|
||||
void open_autocut_silence_dialog();
|
||||
void open_autocut_silence_dialog();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Open the Action Search overlay.
|
||||
*/
|
||||
void open_action_search();
|
||||
void open_action_search();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Clears the current undo stack.
|
||||
*
|
||||
* Clears all current commands in the undo stack. Mostly used for debugging.
|
||||
*/
|
||||
void clear_undo_stack();
|
||||
void clear_undo_stack();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Function called when Olive has finished starting up
|
||||
*
|
||||
* Sets up some last things for Olive that must be run after Olive has completed initialization. If a project was
|
||||
* loaded as a command line argument, it's loaded here.
|
||||
*/
|
||||
void finished_initialize();
|
||||
void finished_initialize();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Save an auto-recovery file of the current project.
|
||||
*
|
||||
* Call this function to save the current state of the project as an auto-recovery project. Called regularly by
|
||||
* `autorecovery_timer`.
|
||||
*/
|
||||
void save_autorecovery_file();
|
||||
void save_autorecovery_file();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Opens the Preferences dialog
|
||||
*/
|
||||
void open_preferences();
|
||||
void open_preferences();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Clear the recent projects list
|
||||
*
|
||||
* Also saves the cleared recent projects to the config file making it permanent.
|
||||
*/
|
||||
void clear_recent_projects();
|
||||
void clear_recent_projects();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Slot for when the primary sequence has changed.
|
||||
*
|
||||
* Usually by opening a sequence or bringing a corresponding
|
||||
* Timeline widget on top.
|
||||
*/
|
||||
void PrimarySequenceChanged();
|
||||
void PrimarySequenceChanged();
|
||||
|
||||
private:
|
||||
/**
|
||||
/**
|
||||
* @brief Internal function to handle loading a project from file
|
||||
*
|
||||
* Start loading a project. Doesn't check if the current project can be closed, doesn't check if the project exists.
|
||||
@@ -422,9 +425,9 @@ private:
|
||||
* beside the original project file so that it does not overwrite the original and so that the user is not working
|
||||
* on the autorecovery project in Olive's application data directory.
|
||||
*/
|
||||
void OpenProjectWorker(QString fn, bool autorecovery);
|
||||
void OpenProjectWorker(QString fn, bool autorecovery);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Create a LoadDialog and start a LoadThread to load data from a project
|
||||
*
|
||||
* Loads data from an Olive project file creating a LoadDialog to show visual information and a LoadThread to load
|
||||
@@ -450,49 +453,49 @@ private:
|
||||
* TRUE if the current project should be closed before opening, FALSE if the project should be imported into the
|
||||
* currently open one.
|
||||
*/
|
||||
void LoadProject(const QString& fn, bool autorecovery);
|
||||
void LoadProject(const QString& fn, bool autorecovery);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Indiscriminately clear the project without prompting the user
|
||||
*
|
||||
* Will clear the entire project without prompting to save. This is dangerous, use new_project() instead for
|
||||
* anything initiated by the user.
|
||||
*/
|
||||
void ClearProject();
|
||||
void ClearProject();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Saves current recent project list to the configuration file
|
||||
*
|
||||
* This should be called whenever the recent projects change so the changes can be persistent.
|
||||
*/
|
||||
void save_recent_projects();
|
||||
void save_recent_projects();
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Internal pasting function
|
||||
*/
|
||||
void PasteInternal(Sequence* s, bool insert);
|
||||
void PasteInternal(Sequence* s, bool insert);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief File filter used for any file dialogs relating to Olive project files.
|
||||
*/
|
||||
QString project_file_filter;
|
||||
QString project_file_filter;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Regular interval to save an auto-recovery project.
|
||||
*/
|
||||
QTimer autorecovery_timer;
|
||||
QTimer autorecovery_timer;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Internal variable set to **TRUE** by main() if a project file was set as an argument
|
||||
*/
|
||||
bool enable_load_project_on_init;
|
||||
bool enable_load_project_on_init;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Internal translator object that interfaces with the currently loaded language file
|
||||
*/
|
||||
std::unique_ptr<QTranslator> translator;
|
||||
std::unique_ptr<QTranslator> translator;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Internal variable for whether the project has changed since the last autorecovery
|
||||
*
|
||||
* Set by set_modified(), which should be called alongside any change made to the project file and is "unset" when
|
||||
@@ -500,45 +503,57 @@ private:
|
||||
* prevents an autorecovery file saving multiple times if the project hasn't actually changed since the last
|
||||
* autorecovery, but still hasn't been saved into the original file yet.
|
||||
*/
|
||||
bool changed_since_last_autorecovery;
|
||||
bool changed_since_last_autorecovery;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Internal variable for rendering state (set by set_rendering_state() and accessed by is_rendering() ).
|
||||
*/
|
||||
bool rendering_;
|
||||
bool rendering_;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Internal variable for the filename to the autorecovery project file
|
||||
*/
|
||||
QString autorecovery_filename;
|
||||
QString autorecovery_filename;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Internal list of recent projects
|
||||
*/
|
||||
QStringList recent_projects;
|
||||
QStringList recent_projects;
|
||||
|
||||
/**
|
||||
* @brief Internal array of selected clips that a menu created by ShowEffectMenu will act on
|
||||
*/
|
||||
QVector<Clip*> effect_menu_selected_clips;
|
||||
|
||||
private slots:
|
||||
|
||||
/**
|
||||
* @brief Receiver for a menu initiated by ShowEffectMenu
|
||||
*
|
||||
* Adds the selected effect/node to the clips in
|
||||
*
|
||||
* @param q
|
||||
*/
|
||||
void EffectMenuAction(QAction* q);
|
||||
};
|
||||
|
||||
namespace olive {
|
||||
/**
|
||||
/**
|
||||
* @brief Object resource for various global functions used throughout Olive
|
||||
*/
|
||||
extern std::unique_ptr<OliveGlobal> Global;
|
||||
extern std::unique_ptr<OliveGlobal> Global;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Currently active project filename
|
||||
*
|
||||
* Filename for the currently active project. Empty means the file has not
|
||||
* been saved yet.
|
||||
*/
|
||||
extern QString ActiveProjectFilename;
|
||||
extern QString ActiveProjectFilename;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Current application name
|
||||
*/
|
||||
extern QString AppName;
|
||||
extern QString AppName;
|
||||
}
|
||||
|
||||
#endif // OLIVEGLOBAL_H
|
||||
|
||||
@@ -147,5 +147,5 @@ int main(int argc, char *argv[]) {
|
||||
w.showMaximized();
|
||||
}
|
||||
|
||||
return a.exec();
|
||||
return a.exec();
|
||||
}
|
||||
|
||||
+2
-42
@@ -47,6 +47,7 @@
|
||||
#include "global/debug.h"
|
||||
#include "global/path.h"
|
||||
#include "ui/mainwindow.h"
|
||||
#include "ui/menu.h"
|
||||
#include "global/math.h"
|
||||
#include "global/clipboard.h"
|
||||
#include "global/config.h"
|
||||
@@ -55,51 +56,10 @@
|
||||
#include "rendering/shadergenerators.h"
|
||||
#include "global/timing.h"
|
||||
#include "nodes/nodes.h"
|
||||
#include "effects/effectloaders.h"
|
||||
|
||||
QVector<NodePtr> olive::node_library;
|
||||
|
||||
/*
|
||||
NodePtr Node::Create(Clip* c) {
|
||||
// must be an internal effect
|
||||
switch (em->internal) {
|
||||
case kTransformEffect: return std::make_shared<TransformEffect>(c, em);
|
||||
case kTextInput: return std::make_shared<TextEffect>(c, em);
|
||||
case kTimecodeEffect: return std::make_shared<TimecodeEffect>(c, em);
|
||||
case kSolidInput: return std::make_shared<SolidEffect>(c, em);
|
||||
case kNoiseInput: return std::make_shared<AudioNoiseEffect>(c, em);
|
||||
case kVolumeEffect: return std::make_shared<VolumeEffect>(c, em);
|
||||
case kPanEffect: return std::make_shared<PanEffect>(c, em);
|
||||
case kToneInput: return std::make_shared<ToneEffect>(c, em);
|
||||
case kShakeEffect: return std::make_shared<ShakeEffect>(c, em);
|
||||
case kCornerPinEffect: return std::make_shared<CornerPinEffect>(c, em);
|
||||
case kFillLeftRightEffect: return std::make_shared<FillLeftRightEffect>(c, em);
|
||||
case kVstEffect: return std::make_shared<VSTHost>(c, em);
|
||||
case kRichTextInput: return std::make_shared<RichTextEffect>(c, em);
|
||||
case kMediaInput: return std::make_shared<NodeMedia>(c, em);
|
||||
case kShaderEffect: return std::make_shared<NodeShader>(c, em);
|
||||
case kImageOutput: return std::make_shared<NodeImageOutput>(c, em);
|
||||
default:
|
||||
qCritical() << "Invalid effect data";
|
||||
QMessageBox::critical(olive::MainWindow,
|
||||
QCoreApplication::translate("Effect", "Invalid effect"),
|
||||
QCoreApplication::translate("Effect", "No candidate for effect '%1'. This effect may be "
|
||||
"corrupt. Try reinstalling it or Olive.").arg(em->name));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
const EffectMeta* Node::GetInternalMeta(int internal_id, int type) {
|
||||
for (int i=0;i<olive::effects.size();i++) {
|
||||
if (olive::effects.at(i).internal == internal_id && olive::effects.at(i).type == type) {
|
||||
return &olive::effects.at(i);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
*/
|
||||
|
||||
Node::Node(Clip* c) :
|
||||
parent_clip(c),
|
||||
flags_(0),
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
NodeImageOutput::NodeImageOutput(Clip *c) :
|
||||
Node(c)
|
||||
{
|
||||
|
||||
EffectRow* input_texture = new EffectRow(this, "texture", tr("Texture"), true, false);
|
||||
input_texture->AddAcceptedNodeInput(olive::nodes::kTexture);
|
||||
}
|
||||
|
||||
QString NodeImageOutput::name()
|
||||
|
||||
@@ -5,6 +5,9 @@ NodeMedia::NodeMedia(Clip* c) :
|
||||
{
|
||||
EffectRow* matrix_input = new EffectRow(this, "matrix", tr("Matrix"), true, false);
|
||||
matrix_input->AddAcceptedNodeInput(olive::nodes::kMatrix);
|
||||
|
||||
EffectRow* texture_output = new EffectRow(this, "texture", tr("Texture"), true, false);
|
||||
texture_output->SetOutputDataType(olive::nodes::kTexture);
|
||||
}
|
||||
|
||||
QString NodeMedia::name()
|
||||
|
||||
+5
-105
@@ -49,6 +49,7 @@
|
||||
#include "ui/icons.h"
|
||||
#include "global/clipboard.h"
|
||||
#include "global/config.h"
|
||||
#include "global/global.h"
|
||||
#include "ui/timelineheader.h"
|
||||
#include "ui/keyframeview.h"
|
||||
#include "ui/resizablescrollbar.h"
|
||||
@@ -90,36 +91,6 @@ void EffectControls::set_zoom(bool in) {
|
||||
}
|
||||
}
|
||||
|
||||
void EffectControls::menu_select(QAction* q) {
|
||||
ComboAction* ca = new ComboAction();
|
||||
for (int i=0;i<selected_clips_.size();i++) {
|
||||
Clip* c = selected_clips_.at(i);
|
||||
if (c->type() == effect_menu_subtype) {
|
||||
NodeType node_type = static_cast<NodeType>(q->data().toInt());
|
||||
if (effect_menu_type == EFFECT_TYPE_TRANSITION) {
|
||||
if (c->opening_transition == nullptr) {
|
||||
ca->append(new AddTransitionCommand(c,
|
||||
nullptr,
|
||||
nullptr,
|
||||
node_type,
|
||||
olive::config.default_transition_length));
|
||||
}
|
||||
if (c->closing_transition == nullptr) {
|
||||
ca->append(new AddTransitionCommand(nullptr,
|
||||
c,
|
||||
nullptr,
|
||||
node_type,
|
||||
olive::config.default_transition_length));
|
||||
}
|
||||
} else {
|
||||
ca->append(new AddEffectCommand(c, nullptr, node_type));
|
||||
}
|
||||
}
|
||||
}
|
||||
olive::undo_stack.push(ca);
|
||||
update_ui(true);
|
||||
}
|
||||
|
||||
void EffectControls::update_keyframes() {
|
||||
for (int i=0;i<open_effects_.size();i++) {
|
||||
EffectUI* ui = open_effects_.at(i);
|
||||
@@ -138,77 +109,6 @@ void EffectControls::scroll_to_frame(long frame) {
|
||||
scroll_to_frame_internal(horizontalScrollBar, frame - keyframeView->visible_in, zoom, keyframeView->width());
|
||||
}
|
||||
|
||||
void EffectControls::show_effect_menu(EffectType type, olive::TrackType subtype) {
|
||||
effect_menu_type = type;
|
||||
effect_menu_subtype = subtype;
|
||||
|
||||
olive::effects_loaded.lock();
|
||||
|
||||
Menu effects_menu(this);
|
||||
effects_menu.setToolTipsVisible(true);
|
||||
|
||||
for (int i=0;i<olive::node_library.size();i++) {
|
||||
|
||||
NodePtr node = olive::node_library.at(i);
|
||||
|
||||
if (node != nullptr && node->type() == type && node->subtype() == subtype) {
|
||||
QAction* action = new QAction(&effects_menu);
|
||||
action->setText(node->name());
|
||||
action->setData(i);
|
||||
if (!node->description().isEmpty()) {
|
||||
action->setToolTip(node->description());
|
||||
}
|
||||
|
||||
QMenu* parent = &effects_menu;
|
||||
if (!node->category().isEmpty()) {
|
||||
bool found = false;
|
||||
for (int j=0;j<effects_menu.actions().size();j++) {
|
||||
QAction* action = effects_menu.actions().at(j);
|
||||
if (action->menu() != nullptr) {
|
||||
if (action->menu()->title() == node->category()) {
|
||||
parent = action->menu();
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
parent = new Menu(&effects_menu);
|
||||
parent->setToolTipsVisible(true);
|
||||
parent->setTitle(node->category());
|
||||
|
||||
bool found = false;
|
||||
for (int i=0;i<effects_menu.actions().size();i++) {
|
||||
QAction* comp_action = effects_menu.actions().at(i);
|
||||
if (comp_action->text() > node->category()) {
|
||||
effects_menu.insertMenu(comp_action, parent);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) effects_menu.addMenu(parent);
|
||||
}
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
for (int i=0;i<parent->actions().size();i++) {
|
||||
QAction* comp_action = parent->actions().at(i);
|
||||
if (comp_action->text() > action->text()) {
|
||||
parent->insertAction(comp_action, action);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) parent->addAction(action);
|
||||
}
|
||||
}
|
||||
|
||||
olive::effects_loaded.unlock();
|
||||
|
||||
connect(&effects_menu, SIGNAL(triggered(QAction*)), this, SLOT(menu_select(QAction*)));
|
||||
effects_menu.exec(QCursor::pos());
|
||||
}
|
||||
|
||||
void EffectControls::UpdateTitle() {
|
||||
if (selected_clips_.isEmpty()) {
|
||||
setWindowTitle(panel_name + tr("(none)"));
|
||||
@@ -445,19 +345,19 @@ bool EffectControls::focused()
|
||||
}
|
||||
|
||||
void EffectControls::video_effect_click() {
|
||||
show_effect_menu(EFFECT_TYPE_EFFECT, olive::kTypeVideo);
|
||||
olive::Global->ShowEffectMenu(EFFECT_TYPE_EFFECT, olive::kTypeVideo, selected_clips_);
|
||||
}
|
||||
|
||||
void EffectControls::audio_effect_click() {
|
||||
show_effect_menu(EFFECT_TYPE_EFFECT, olive::kTypeAudio);
|
||||
olive::Global->ShowEffectMenu(EFFECT_TYPE_EFFECT, olive::kTypeAudio, selected_clips_);
|
||||
}
|
||||
|
||||
void EffectControls::video_transition_click() {
|
||||
show_effect_menu(EFFECT_TYPE_TRANSITION, olive::kTypeVideo);
|
||||
olive::Global->ShowEffectMenu(EFFECT_TYPE_TRANSITION, olive::kTypeVideo, selected_clips_);
|
||||
}
|
||||
|
||||
void EffectControls::audio_transition_click() {
|
||||
show_effect_menu(EFFECT_TYPE_TRANSITION, olive::kTypeAudio);
|
||||
olive::Global->ShowEffectMenu(EFFECT_TYPE_TRANSITION, olive::kTypeAudio, selected_clips_);
|
||||
}
|
||||
|
||||
void EffectControls::resizeEvent(QResizeEvent*) {
|
||||
|
||||
@@ -76,8 +76,6 @@ public:
|
||||
public slots:
|
||||
void update_keyframes();
|
||||
private slots:
|
||||
void menu_select(QAction* q);
|
||||
|
||||
void video_effect_click();
|
||||
void audio_effect_click();
|
||||
void video_transition_click();
|
||||
@@ -94,14 +92,11 @@ protected:
|
||||
virtual void ClearEvent() override;
|
||||
virtual void LoadEvent() override;
|
||||
private:
|
||||
void show_effect_menu(EffectType type, olive::TrackType subtype);
|
||||
void load_keyframes();
|
||||
void UpdateTitle();
|
||||
|
||||
void setup_ui();
|
||||
|
||||
int effect_menu_type;
|
||||
olive::TrackType effect_menu_subtype;
|
||||
QString panel_name;
|
||||
|
||||
QWidget* video_effect_area;
|
||||
|
||||
@@ -16,6 +16,8 @@ EffectsPanel::~EffectsPanel()
|
||||
}
|
||||
|
||||
void EffectsPanel::Clear(bool clear_cache) {
|
||||
AboutToClearEvent();
|
||||
|
||||
// clear existing clips
|
||||
deselect_all_effects(nullptr);
|
||||
|
||||
@@ -205,6 +207,10 @@ void EffectsPanel::copy(bool del) {
|
||||
}
|
||||
}
|
||||
|
||||
void EffectsPanel::AboutToClearEvent()
|
||||
{
|
||||
}
|
||||
|
||||
void EffectsPanel::DeleteEffect(ComboAction* ca, Node* effect_ref) {
|
||||
if (effect_ref->type() == EFFECT_TYPE_EFFECT) {
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ public slots:
|
||||
void cut();
|
||||
void copy(bool del = false);
|
||||
protected:
|
||||
virtual void AboutToClearEvent();
|
||||
virtual void ClearEvent();
|
||||
virtual void LoadEvent();
|
||||
|
||||
|
||||
+19
-4
@@ -6,6 +6,8 @@
|
||||
#include <QGraphicsProxyWidget>
|
||||
#include <QDebug>
|
||||
|
||||
#include "global/global.h"
|
||||
|
||||
NodeEditor::NodeEditor(QWidget *parent) :
|
||||
EffectsPanel(parent),
|
||||
view_(&scene_)
|
||||
@@ -26,6 +28,12 @@ NodeEditor::NodeEditor(QWidget *parent) :
|
||||
view_.setDragMode(QGraphicsView::RubberBandDrag);
|
||||
|
||||
connect(&scene_, SIGNAL(changed(const QList<QRectF>&)), this, SLOT(ItemsChanged()));
|
||||
connect(&view_, SIGNAL(RequestContextMenu()), this, SLOT(ContextMenu()));
|
||||
}
|
||||
|
||||
NodeEditor::~NodeEditor()
|
||||
{
|
||||
Clear(true);
|
||||
}
|
||||
|
||||
void NodeEditor::Retranslate()
|
||||
@@ -60,10 +68,10 @@ void NodeEditor::LoadEvent()
|
||||
LoadEdges();
|
||||
}
|
||||
|
||||
void NodeEditor::ClearEvent()
|
||||
void NodeEditor::AboutToClearEvent()
|
||||
{
|
||||
foreach (NodeUI* node, nodes_) {
|
||||
scene_.removeItem(node);
|
||||
delete node;
|
||||
}
|
||||
|
||||
nodes_.clear();
|
||||
@@ -75,6 +83,7 @@ void NodeEditor::ClearEdges()
|
||||
{
|
||||
foreach (NodeEdgeUI* edge, edges_) {
|
||||
scene_.removeItem(edge);
|
||||
delete edge;
|
||||
}
|
||||
|
||||
edges_.clear();
|
||||
@@ -150,8 +159,14 @@ void NodeEditor::ItemsChanged()
|
||||
|
||||
void NodeEditor::ReloadEdges()
|
||||
{
|
||||
qDebug() << "reload edges called";
|
||||
|
||||
ClearEdges();
|
||||
LoadEdges();
|
||||
}
|
||||
|
||||
void NodeEditor::ContextMenu()
|
||||
{
|
||||
if (!open_effects_.isEmpty()) {
|
||||
Clip* c = open_effects_.first()->GetEffect()->parent_clip;
|
||||
olive::Global->ShowEffectMenu(EFFECT_TYPE_EFFECT, c->type(), {c});
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -12,12 +12,13 @@ class NodeEditor : public EffectsPanel {
|
||||
Q_OBJECT
|
||||
public:
|
||||
NodeEditor(QWidget* parent = nullptr);
|
||||
virtual ~NodeEditor() override;
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
protected:
|
||||
virtual void LoadEvent() override;
|
||||
virtual void ClearEvent() override;
|
||||
virtual void AboutToClearEvent() override;
|
||||
|
||||
private:
|
||||
QGraphicsScene scene_;
|
||||
@@ -35,6 +36,7 @@ private:
|
||||
private slots:
|
||||
void ItemsChanged();
|
||||
void ReloadEdges();
|
||||
void ContextMenu();
|
||||
|
||||
};
|
||||
|
||||
|
||||
+6
-11
@@ -873,22 +873,24 @@ void Timeline::transition_tool_click() {
|
||||
|
||||
Menu transition_menu(this);
|
||||
|
||||
transition_menu.addAction(tr("Video Transitions"))->setEnabled(false);
|
||||
|
||||
for (int i=0;i<olive::node_library.size();i++) {
|
||||
NodePtr node = olive::node_library.at(i);
|
||||
if (node->type() == EFFECT_TYPE_TRANSITION && node->subtype() == olive::kTypeVideo) {
|
||||
if (node != nullptr && node->type() == EFFECT_TYPE_TRANSITION && node->subtype() == olive::kTypeVideo) {
|
||||
QAction* a = transition_menu.addAction(node->name());
|
||||
a->setObjectName("v");
|
||||
a->setData(i);
|
||||
}
|
||||
}
|
||||
|
||||
transition_menu.addSeparator();
|
||||
|
||||
transition_menu.addAction(tr("Audio Transitions"))->setEnabled(false);
|
||||
|
||||
for (int i=0;i<olive::node_library.size();i++) {
|
||||
NodePtr node = olive::node_library.at(i);
|
||||
if (node->type() == EFFECT_TYPE_TRANSITION && node->subtype() == olive::kTypeVideo) {
|
||||
if (node != nullptr && node->type() == EFFECT_TYPE_TRANSITION && node->subtype() == olive::kTypeAudio) {
|
||||
QAction* a = transition_menu.addAction(node->name());
|
||||
a->setObjectName("a");
|
||||
a->setData(i);
|
||||
}
|
||||
}
|
||||
@@ -902,13 +904,6 @@ void Timeline::transition_tool_click() {
|
||||
|
||||
void Timeline::transition_menu_select(QAction* a) {
|
||||
transition_tool_meta = static_cast<NodeType>(a->data().toInt());
|
||||
|
||||
if (a->objectName() == "v") {
|
||||
transition_tool_side = olive::kTypeVideo;
|
||||
} else {
|
||||
transition_tool_side = olive::kTypeAudio;
|
||||
}
|
||||
|
||||
timeline_area->setCursor(Qt::CrossCursor);
|
||||
olive::timeline::current_tool = olive::timeline::TIMELINE_TOOL_TRANSITION;
|
||||
toolTransitionButton->setChecked(true);
|
||||
|
||||
@@ -115,7 +115,6 @@ public:
|
||||
Clip* transition_tool_open_clip;
|
||||
Clip* transition_tool_close_clip;
|
||||
NodeType transition_tool_meta;
|
||||
olive::TrackType transition_tool_side;
|
||||
|
||||
// hand tool variables
|
||||
bool hand_moving;
|
||||
|
||||
+5
-2
@@ -29,8 +29,8 @@ NodeUI::NodeUI() :
|
||||
|
||||
NodeUI::~NodeUI()
|
||||
{
|
||||
if (scene() != nullptr) {
|
||||
scene()->removeItem(proxy_);
|
||||
if (proxy_ != nullptr) {
|
||||
proxy_->setParentItem(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,6 +164,7 @@ void NodeUI::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
||||
if (clicked_socket_ > -1) {
|
||||
|
||||
bool line_is_touching_node = false;
|
||||
drag_destination_ = nullptr;
|
||||
|
||||
QPointF drag_line_end = event->scenePos();
|
||||
|
||||
@@ -234,6 +235,8 @@ void NodeUI::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
if (clicked_socket_ > -1) {
|
||||
scene()->removeItem(drag_line_);
|
||||
delete drag_line_;
|
||||
|
||||
event->accept();
|
||||
|
||||
if (drag_destination_ != nullptr) {
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
#include <QScrollBar>
|
||||
#include <QDebug>
|
||||
|
||||
#include "ui/menu.h"
|
||||
#include "nodes/node.h"
|
||||
|
||||
NodeView::NodeView(QGraphicsScene *scene, QWidget *parent) :
|
||||
QGraphicsView(scene, parent),
|
||||
hand_moving_(false)
|
||||
@@ -20,6 +23,11 @@ void NodeView::mousePressEvent(QMouseEvent *event)
|
||||
if (event->button() == Qt::MidButton) {
|
||||
hand_moving_ = true;
|
||||
drag_start_ = event->pos();
|
||||
} else if (event->button() == Qt::RightButton && scene()->itemAt(mapToScene(event->pos()), QTransform()) == nullptr) {
|
||||
|
||||
// If the user clicked with the right button on empty space, show the context menu
|
||||
emit RequestContextMenu();
|
||||
|
||||
} else {
|
||||
QGraphicsView::mousePressEvent(event);
|
||||
}
|
||||
|
||||
@@ -3,11 +3,16 @@
|
||||
|
||||
#include <QGraphicsView>
|
||||
|
||||
#include "timeline/tracktypes.h"
|
||||
|
||||
class NodeView : public QGraphicsView {
|
||||
Q_OBJECT
|
||||
public:
|
||||
NodeView(QGraphicsScene *scene, QWidget* parent = nullptr);
|
||||
|
||||
signals:
|
||||
void RequestContextMenu();
|
||||
|
||||
protected:
|
||||
virtual void mousePressEvent(QMouseEvent *event) override;
|
||||
virtual void mouseMoveEvent(QMouseEvent *event) override;
|
||||
|
||||
+1
-1
@@ -2741,7 +2741,7 @@ void TimelineView::mouseMoveEvent(QMouseEvent *event) {
|
||||
// cursor is hovering over a clip
|
||||
|
||||
// check if the clip and transition are both the same sign (meaning video/audio are the same)
|
||||
if (track_list_->type() == ParentTimeline()->transition_tool_side) {
|
||||
if (track_list_->type() == olive::node_library[ParentTimeline()->transition_tool_meta]->subtype()) {
|
||||
|
||||
// the range within which the transition tool will assume the user wants to make a shared transition
|
||||
// between two clips rather than just one transition on one clip
|
||||
|
||||
Reference in New Issue
Block a user