From 6bf62017eabc1c7d444d20e1ab6e7ab8d2b53c5d Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 13 Apr 2019 15:57:09 +1000 Subject: [PATCH] right click menu in node view for adding nodes --- global/global.cpp | 107 ++++++++++++++ global/global.h | 255 +++++++++++++++++--------------- main.cpp | 2 +- nodes/node.cpp | 44 +----- nodes/nodes/nodeimageoutput.cpp | 3 +- nodes/nodes/nodemedia.cpp | 3 + panels/effectcontrols.cpp | 110 +------------- panels/effectcontrols.h | 5 - panels/effectspanel.cpp | 6 + panels/effectspanel.h | 1 + panels/nodeeditor.cpp | 23 ++- panels/nodeeditor.h | 4 +- panels/timeline.cpp | 17 +-- panels/timeline.h | 1 - ui/nodeui.cpp | 7 +- ui/nodeview.cpp | 8 + ui/nodeview.h | 5 + ui/timelineview.cpp | 2 +- 18 files changed, 309 insertions(+), 294 deletions(-) diff --git a/global/global.cpp b/global/global.cpp index a98e82772..c17e8ec91 100644 --- a/global/global.cpp +++ b/global/global.cpp @@ -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(q->data().toInt()); + Node* n = olive::node_library[node_type].get(); + + for (int i=0;itype() == 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 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;itype() == 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;jmenu() != 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;itext() > node->category()) { + effects_menu.insertMenu(comp_action, parent); + found = true; + break; + } + } + if (!found) effects_menu.addMenu(parent); + } + } + + bool found = false; + for (int i=0;iactions().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()) { diff --git a/global/global.h b/global/global.h index a8a0ee47f..f4434cf79 100644 --- a/global/global.h +++ b/global/global.h @@ -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 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 translator; + std::unique_ptr 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 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 Global; +extern std::unique_ptr 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 diff --git a/main.cpp b/main.cpp index e387d92e0..7b4cfeeee 100644 --- a/main.cpp +++ b/main.cpp @@ -147,5 +147,5 @@ int main(int argc, char *argv[]) { w.showMaximized(); } - return a.exec(); + return a.exec(); } diff --git a/nodes/node.cpp b/nodes/node.cpp index 8f2d95deb..17c11c6c6 100644 --- a/nodes/node.cpp +++ b/nodes/node.cpp @@ -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 olive::node_library; -/* -NodePtr Node::Create(Clip* c) { - // must be an internal effect - switch (em->internal) { - case kTransformEffect: return std::make_shared(c, em); - case kTextInput: return std::make_shared(c, em); - case kTimecodeEffect: return std::make_shared(c, em); - case kSolidInput: return std::make_shared(c, em); - case kNoiseInput: return std::make_shared(c, em); - case kVolumeEffect: return std::make_shared(c, em); - case kPanEffect: return std::make_shared(c, em); - case kToneInput: return std::make_shared(c, em); - case kShakeEffect: return std::make_shared(c, em); - case kCornerPinEffect: return std::make_shared(c, em); - case kFillLeftRightEffect: return std::make_shared(c, em); - case kVstEffect: return std::make_shared(c, em); - case kRichTextInput: return std::make_shared(c, em); - case kMediaInput: return std::make_shared(c, em); - case kShaderEffect: return std::make_shared(c, em); - case kImageOutput: return std::make_shared(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;iAddAcceptedNodeInput(olive::nodes::kTexture); } QString NodeImageOutput::name() diff --git a/nodes/nodes/nodemedia.cpp b/nodes/nodes/nodemedia.cpp index e835a884a..f5b3e3272 100644 --- a/nodes/nodes/nodemedia.cpp +++ b/nodes/nodes/nodemedia.cpp @@ -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() diff --git a/panels/effectcontrols.cpp b/panels/effectcontrols.cpp index bc81c16a2..b06716e8b 100644 --- a/panels/effectcontrols.cpp +++ b/panels/effectcontrols.cpp @@ -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;itype() == effect_menu_subtype) { - NodeType node_type = static_cast(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;ivisible_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;itype() == 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;jmenu() != 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;itext() > node->category()) { - effects_menu.insertMenu(comp_action, parent); - found = true; - break; - } - } - if (!found) effects_menu.addMenu(parent); - } - } - - bool found = false; - for (int i=0;iactions().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*) { diff --git a/panels/effectcontrols.h b/panels/effectcontrols.h index 1387744f4..94f41eb72 100644 --- a/panels/effectcontrols.h +++ b/panels/effectcontrols.h @@ -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; diff --git a/panels/effectspanel.cpp b/panels/effectspanel.cpp index 3c76db66b..fab4f34ef 100644 --- a/panels/effectspanel.cpp +++ b/panels/effectspanel.cpp @@ -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) { diff --git a/panels/effectspanel.h b/panels/effectspanel.h index e3d52bd5e..1100dd94f 100644 --- a/panels/effectspanel.h +++ b/panels/effectspanel.h @@ -25,6 +25,7 @@ public slots: void cut(); void copy(bool del = false); protected: + virtual void AboutToClearEvent(); virtual void ClearEvent(); virtual void LoadEvent(); diff --git a/panels/nodeeditor.cpp b/panels/nodeeditor.cpp index 7c5eebeba..387559a94 100644 --- a/panels/nodeeditor.cpp +++ b/panels/nodeeditor.cpp @@ -6,6 +6,8 @@ #include #include +#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&)), 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}); + } +} diff --git a/panels/nodeeditor.h b/panels/nodeeditor.h index 4de6d70de..b6cb01316 100644 --- a/panels/nodeeditor.h +++ b/panels/nodeeditor.h @@ -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(); }; diff --git a/panels/timeline.cpp b/panels/timeline.cpp index e23f7e125..0cb4c0f1b 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -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;itype() == 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;itype() == 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(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); diff --git a/panels/timeline.h b/panels/timeline.h index ba47ca69d..1830c6bd4 100644 --- a/panels/timeline.h +++ b/panels/timeline.h @@ -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; diff --git a/ui/nodeui.cpp b/ui/nodeui.cpp index 17180ec3f..08ef0b877 100644 --- a/ui/nodeui.cpp +++ b/ui/nodeui.cpp @@ -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) { diff --git a/ui/nodeview.cpp b/ui/nodeview.cpp index 94366749d..b3c33f413 100644 --- a/ui/nodeview.cpp +++ b/ui/nodeview.cpp @@ -4,6 +4,9 @@ #include #include +#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); } diff --git a/ui/nodeview.h b/ui/nodeview.h index bd751aa4c..648aef23d 100644 --- a/ui/nodeview.h +++ b/ui/nodeview.h @@ -3,11 +3,16 @@ #include +#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; diff --git a/ui/timelineview.cpp b/ui/timelineview.cpp index 403410de0..ed6bc07f5 100644 --- a/ui/timelineview.cpp +++ b/ui/timelineview.cpp @@ -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