#ifndef XTC_SEQUENCER_WIDGET_HPP #define XTC_SEQUENCER_WIDGET_HPP #include #include "xtc/xtc_audio.h" #include #include #include class SequencerWidget : public Fl_Widget { public: /** * @brief Prepares a model edit using the widget's opaque callback context. * * @return XTC_OK to allow the edit, or an error to cancel it. */ xtc_result duplicate_selection(); xtc_result split_selection(); xtc_result audio_properties(float gain, uint64_t fade_in, uint64_t fade_out); void audio_properties_dialog(); void refresh_waveforms(); size_t waveform_count() const { return waves_.size(); } xtc_result rename_selection(const char *name); xtc_result move_track(int direction); xtc_result grid(uint64_t ticks); uint64_t grid() const { return grid_; } using BeforeEdit = xtc_result (*)(void *); BeforeEdit before_edit = nullptr; /** * @brief Returns the selected track identifier, or UINT64_MAX when none is selected. * @return Stable track identifier, or UINT64_MAX if no track is selected. */ uint64_t selected_track() const { return track_; } /** * @brief Returns the selected clip identifier, or UINT64_MAX when none is selected. * @return Stable clip identifier, or UINT64_MAX if no clip is selected. */ uint64_t selected_clip() const { return clip_; } /** * @brief Requests a transport seek using an opaque context and musical tick. * * @return XTC_OK on success, or a transport error. */ using Seek = xtc_result (*)(void *, uint64_t); /** * @brief Constructs the timeline widget with a borrowed project and seek callback. * * @param x Left coordinate in pixels. * @param y Top coordinate in pixels. * @param project Borrowed project; must outlive the widget's use of it. * @param seek Callback used to request a transport seek. * @param context Opaque callback context. */ SequencerWidget(int x, int y, xtc_project *project, Seek seek, void *context); /** * @brief Replaces the borrowed project and refreshes the widget's displayed state. * * @param project Borrowed project; must outlive the widget's use of it. * @param reset Whether to clear selection, cursor, and drag state. */ void set_project(xtc_project *project, bool reset = true); /** * @brief Returns the timeline cursor position in musical ticks. * @return Cursor position in musical ticks. */ uint64_t position() const { return cursor_; } /** * @brief Moves the timeline cursor to the supplied musical tick and requests a redraw. * * @param tick Musical position in ticks. */ void position(uint64_t tick) { cursor_ = tick; redraw(); } /** * @brief Creates and selects a track through the arrangement edit hook. * @return XTC_OK on success, or an error code describing the failure. */ xtc_result add_track(); /** * @brief Creates and selects a one-bar silent region at the timeline cursor. * @return XTC_OK on success, or an error code describing the failure. */ xtc_result add_region(); /** * @brief Creates and selects a one-bar MIDI clip at the timeline cursor. * @return XTC_OK on success, or an error code describing the failure. */ xtc_result add_midi_clip(); /** * @brief Removes the selected clip, or the selected track when no clip is selected. * @return XTC_OK on success, or an error code describing the failure. */ xtc_result delete_selection(); /** * @brief Adjusts timeline zoom within its supported bounds and updates the scroll extent. * * @param direction Zoom direction; positive zooms in, nonpositive zooms out. */ void zoom(int direction); /** * @brief Returns the borrowed project currently displayed by the widget. * @return Borrowed project pointer. */ xtc_project *project() const { return project_; } /** * @brief Handles FLTK input events and delegates unhandled events to the base widget. * * @param event FLTK event code. * @return Nonzero if the event was handled, zero otherwise. */ int handle(int event) override; protected: /** * @brief Draws the widget's current model, selection, and interaction feedback. */ void draw() override; private: struct Wave { std::string path; std::unique_ptr data{nullptr, xtc_waveform_destroy}; }; std::vector waves_; void draw_waveform(const xtc_clip_info &clip, int left, int top); xtc_project *project_; Seek seek_; void *context_; uint64_t track_ = UINT64_MAX, clip_ = UINT64_MAX, cursor_ = 0, grid_ = 240; int scale_ = 48, drag_x_ = 0, mode_ = 0; xtc_clip_info original_{}; /** * @brief Converts a screen x coordinate to a bounded timeline tick snapped to a sixteenth note. * * @param px Screen x coordinate in pixels. * @return Snapped tick within the supported project range. */ uint64_t tick(int px) const; /** * @brief Reports an edit result, refreshes the widget, and notifies listeners on success. * * @param result Result of the attempted operation. */ void changed(xtc_result result); /** * @brief Resizes the timeline's scrollable area to fit its tracks and clip ends. */ void extent(); }; #endif