Aegisub/aegisub/src/visual_tool.h

223 lines
7.8 KiB
C++

// Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
/// @file visual_tool.h
/// @see visual_tool.cpp
/// @ingroup visual_ts
#pragma once
#include <deque>
#include <list>
#include <map>
#include <set>
#include <vector>
#include <boost/container/list.hpp>
#include <wx/event.h>
#include <libaegisub/signal.h>
#include "gl_wrap.h"
#include "selection_controller.h"
#include "vector2d.h"
class AssDialogue;
class SubtitlesGrid;
class VideoDisplay;
class wxToolBar;
namespace agi {
struct Context;
class OptionValue;
}
/// @class VisualToolBase
/// @brief Base class for visual tools containing all functionality that doesn't interact with features
///
/// This is required so that visual tools can be used polymorphically, as
/// different VisualTool<T>s are unrelated types otherwise. In addition, as much
/// functionality as possible is implemented here to avoid having four copies
/// of each method for no good reason (and four times as many error messages)
class VisualToolBase {
void OnCommit(int type);
void OnSeek(int new_frame);
void OnMouseCaptureLost(wxMouseCaptureLostEvent &);
/// @brief Get the dialogue line currently in the edit box
/// @return nullptr if the line is not active on the current frame
AssDialogue *GetActiveDialogueLine();
// SubtitleSelectionListener implementation
void OnActiveLineChanged(AssDialogue *new_line);
// Below here are the virtuals that must be implemented
/// Called when the script, video or screen resolutions change
virtual void OnCoordinateSystemsChanged() { DoRefresh(); }
/// Called when the file is changed by something other than a visual tool
virtual void OnFileChanged() { DoRefresh(); }
/// Called when the frame number changes
virtual void OnFrameChanged() { }
/// Called when the active line changes
virtual void OnLineChanged() { DoRefresh(); }
/// Generic refresh to simplify tools which have no interesting state and
/// can simply do do the same thing for any external change (i.e. most of
/// them). Called only by the above virtual methods.
virtual void DoRefresh() { }
protected:
std::deque<agi::signal::Connection> connections;
OpenGLWrapper gl;
/// Called when the user double-clicks
virtual void OnDoubleClick() { }
static const wxColour colour[4];
agi::Context *c;
VideoDisplay *parent;
bool holding; ///< Is a hold currently in progress?
AssDialogue *active_line; ///< Active dialogue line; nullptr if it is not visible on the current frame
bool dragging; ///< Is a drag currently in progress?
int frame_number; ///< Current frame number
bool shift_down; ///< Is shift down?
bool ctrl_down; ///< Is ctrl down?
bool alt_down; ///< Is alt down?
Vector2D mouse_pos; ///< Last seen mouse position
Vector2D drag_start; ///< Mouse position at the beginning of the last drag
Vector2D script_res; ///< Script resolution
Vector2D video_pos; ///< Top-left corner of the video in the display area
Vector2D video_res; ///< Video resolution
agi::signal::Connection file_changed_connection;
int commit_id; ///< Last used commit id for coalescing
/// @brief Commit the current file state
/// @param message Description of changes for undo
void Commit(wxString message = wxString());
bool IsDisplayed(AssDialogue *line) const;
/// Get the line's position if it's set, or it's default based on style if not
Vector2D GetLinePosition(AssDialogue *diag);
/// Get the line's origin if it's set, or Vector2D::Bad() if not
Vector2D GetLineOrigin(AssDialogue *diag);
bool GetLineMove(AssDialogue *diag, Vector2D &p1, Vector2D &p2, int &t1, int &t2);
void GetLineRotation(AssDialogue *diag, float &rx, float &ry, float &rz);
void GetLineScale(AssDialogue *diag, Vector2D &scale);
void GetLineClip(AssDialogue *diag, Vector2D &p1, Vector2D &p2, bool &inverse);
std::string GetLineVectorClip(AssDialogue *diag, int &scale, bool &inverse);
void SetOverride(AssDialogue* line, std::string const& tag, std::string const& value);
void SetSelectedOverride(std::string const& tag, std::string const& value);
VisualToolBase(VideoDisplay *parent, agi::Context *context);
public:
/// Convert a point from video to script coordinates
Vector2D ToScriptCoords(Vector2D point) const;
/// Convert a point from script to video coordinates
Vector2D FromScriptCoords(Vector2D point) const;
// Stuff called by VideoDisplay
virtual void OnMouseEvent(wxMouseEvent &event)=0;
virtual void Draw()=0;
virtual void SetDisplayArea(int x, int y, int w, int h);
virtual void SetToolbar(wxToolBar *) { }
virtual ~VisualToolBase() { }
};
/// Visual tool base class containing all common feature-related functionality
template<class FeatureType>
class VisualTool : public VisualToolBase {
protected:
typedef FeatureType Feature;
typedef typename boost::container::list<FeatureType>::iterator feature_iterator;
typedef typename boost::container::list<FeatureType>::const_iterator feature_const_iterator;
private:
struct ltaddr {
template<class T>
bool operator()(T lft, T rgt) const {
return &*lft < &*rgt;
}
};
boost::container::list<agi::signal::Connection> slots;
typedef typename std::set<feature_iterator, ltaddr>::iterator selection_iterator;
bool sel_changed; /// Has the selection already been changed in the current click?
/// @brief Called when a hold is begun
/// @return Should the hold actually happen?
virtual bool InitializeHold() { return false; }
/// @brief Called on every mouse event during a hold
virtual void UpdateHold() { }
/// @brief Called at the beginning of a drag
/// @param feature The visual feature clicked on
/// @return Should the drag happen?
virtual bool InitializeDrag(feature_iterator feature) { return true; }
/// @brief Called on every mouse event during a drag
/// @param feature The current feature to process; not necessarily the one clicked on
virtual void UpdateDrag(feature_iterator feature) { }
/// @brief Draw stuff
virtual void Draw()=0;
protected:
std::set<feature_iterator, ltaddr> sel_features; ///< Currently selected visual features
typedef typename std::set<feature_iterator, ltaddr>::const_iterator sel_iterator;
/// Topmost feature under the mouse; generally only valid during a drag
feature_iterator active_feature;
/// List of features which are drawn and can be clicked on
/// List is used here for the iterator invalidation properties
boost::container::list<FeatureType> features;
/// Draw all of the features in the list
void DrawAllFeatures();
/// @brief Remove a feature from the selection
/// @param i Index in the feature list
/// Also deselects lines if all features for that line have been deselected
void RemoveSelection(feature_iterator feat);
/// @brief Set the selection to a single feature, deselecting everything else
/// @param i Index in the feature list
void SetSelection(feature_iterator feat, bool clear);
public:
/// @brief Handler for all mouse events
/// @param event Shockingly enough, the mouse event
void OnMouseEvent(wxMouseEvent &event);
/// @brief Constructor
/// @param parent The VideoDisplay to use for coordinate conversion
/// @param video Video and mouse information passing blob
VisualTool(VideoDisplay *parent, agi::Context *context);
};