forked from mia/Aegisub
be77dc8307
Convert all coordinates within the visual tools to Vector2D, which has been significantly extended. Eliminates a lot of issues with accumulated rounding errors and simplifies a lot of code. Modernize the visual tools' interactions with the rest of Aegisub by connecting to signals directly rather than routing everything through the video display and converting the main visual tool mode toolbar to the command system. Extract all references to OpenGL from the visual tools and move them to OpenGLWrapper as a first step towards making it possible to implement an alternative video renderer. In the process, eliminate all uses of OpenGL immediate mode. Fix a bunch of minor issues and general instability. Originally committed to SVN as r5823.
311 lines
8.9 KiB
C++
311 lines
8.9 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/
|
|
//
|
|
// $Id$
|
|
|
|
/// @file visual_tool_drag.cpp
|
|
/// @brief Position all visible subtitles by dragging visual typesetting tool
|
|
/// @ingroup visual_ts
|
|
|
|
#include "config.h"
|
|
|
|
#include "visual_tool_drag.h"
|
|
|
|
#ifndef AGI_PRE
|
|
#include <wx/bmpbuttn.h>
|
|
#include <wx/toolbar.h>
|
|
#endif
|
|
|
|
#include "ass_dialogue.h"
|
|
#include "ass_file.h"
|
|
#include "include/aegisub/context.h"
|
|
#include "libresrc/libresrc.h"
|
|
#include "utils.h"
|
|
#include "video_context.h"
|
|
|
|
static const DraggableFeatureType DRAG_ORIGIN = DRAG_BIG_TRIANGLE;
|
|
static const DraggableFeatureType DRAG_START = DRAG_BIG_SQUARE;
|
|
static const DraggableFeatureType DRAG_END = DRAG_BIG_CIRCLE;
|
|
|
|
VisualToolDrag::VisualToolDrag(VideoDisplay *parent, agi::Context *context)
|
|
: VisualTool<VisualToolDragDraggableFeature>(parent, context)
|
|
, primary(0)
|
|
, button_is_move(true)
|
|
{
|
|
c->selectionController->GetSelectedSet(selection);
|
|
}
|
|
|
|
void VisualToolDrag::SetToolbar(wxToolBar *tb) {
|
|
toolbar = tb;
|
|
toolbar->AddTool(-1, _("Toggle between \\move and \\pos"), GETIMAGE(visual_move_conv_move_24));
|
|
toolbar->Realize();
|
|
toolbar->Show(true);
|
|
|
|
toolbar->Bind(wxEVT_COMMAND_TOOL_CLICKED, &VisualToolDrag::OnSubTool, this);
|
|
}
|
|
|
|
void VisualToolDrag::UpdateToggleButtons() {
|
|
bool to_move = true;
|
|
if (active_line) {
|
|
Vector2D p1, p2;
|
|
int t1, t2;
|
|
to_move = !GetLineMove(active_line, p1, p2, t1, t2);
|
|
}
|
|
|
|
if (to_move == button_is_move) return;
|
|
|
|
toolbar->SetToolNormalBitmap(toolbar->GetToolByPos(0)->GetId(),
|
|
to_move ? GETIMAGE(visual_move_conv_move_24) : GETIMAGE(visual_move_conv_pos_24));
|
|
button_is_move = to_move;
|
|
}
|
|
|
|
void VisualToolDrag::OnSubTool(wxCommandEvent &) {
|
|
// Toggle \move <-> \pos
|
|
VideoContext *vc = c->videoController;
|
|
for (Selection::const_iterator cur = selection.begin(); cur != selection.end(); ++cur) {
|
|
AssDialogue *line = *cur;
|
|
Vector2D p1, p2;
|
|
int t1, t2;
|
|
|
|
bool has_move = GetLineMove(line, p1, p2, t1, t2);
|
|
|
|
if (has_move)
|
|
SetOverride(line, "\\pos", p1.PStr());
|
|
else {
|
|
p1 = GetLinePosition(line);
|
|
// Round the start and end times to exact frames
|
|
int start = vc->TimeAtFrame(vc->FrameAtTime(line->Start.GetMS(), agi::vfr::START)) - line->Start.GetMS();
|
|
int end = vc->TimeAtFrame(vc->FrameAtTime(line->Start.GetMS(), agi::vfr::END)) - line->Start.GetMS();
|
|
SetOverride(line, "\\move", wxString::Format("(%s,%s,%d,%d)", p1.Str(), p1.Str(), start, end));
|
|
}
|
|
}
|
|
|
|
Commit();
|
|
OnFileChanged();
|
|
UpdateToggleButtons();
|
|
}
|
|
|
|
void VisualToolDrag::OnLineChanged() {
|
|
UpdateToggleButtons();
|
|
}
|
|
|
|
void VisualToolDrag::OnFileChanged() {
|
|
/// @todo it should be possible to preserve the selection in some cases
|
|
features.clear();
|
|
sel_features.clear();
|
|
primary = 0;
|
|
active_feature = features.end();
|
|
|
|
for (entryIter it = c->ass->Line.begin(); it != c->ass->Line.end(); ++it) {
|
|
AssDialogue *diag = dynamic_cast<AssDialogue*>(*it);
|
|
if (diag && IsDisplayed(diag))
|
|
MakeFeatures(diag);
|
|
}
|
|
|
|
UpdateToggleButtons();
|
|
}
|
|
|
|
void VisualToolDrag::OnFrameChanged() {
|
|
if (primary && !IsDisplayed(primary->line))
|
|
primary = 0;
|
|
|
|
feature_iterator feat = features.begin();
|
|
feature_iterator end = features.end();
|
|
|
|
for (entryIter it = c->ass->Line.begin(); it != c->ass->Line.end(); ++it) {
|
|
AssDialogue *diag = dynamic_cast<AssDialogue*>(*it);
|
|
if (!diag) continue;
|
|
|
|
if (IsDisplayed(diag)) {
|
|
// Features don't exist and should
|
|
if (feat == end || feat->line != diag)
|
|
MakeFeatures(diag, feat);
|
|
// Move past already existing features for the line
|
|
else
|
|
while (feat != end && feat->line == diag) ++feat;
|
|
}
|
|
else {
|
|
// Remove all features for this line (if any)
|
|
while (feat != end && feat->line == diag) {
|
|
feat->line = 0;
|
|
RemoveSelection(feat);
|
|
feat = features.erase(feat);
|
|
}
|
|
}
|
|
}
|
|
|
|
active_feature = features.end();
|
|
}
|
|
|
|
void VisualToolDrag::OnSelectedSetChanged(const Selection &added, const Selection &removed) {
|
|
c->selectionController->GetSelectedSet(selection);
|
|
|
|
for (feature_iterator it = features.begin(); it != features.end(); ++it) {
|
|
if (removed.count(it->line))
|
|
sel_features.erase(it);
|
|
else if (added.count(it->line) && it->type == DRAG_START)
|
|
sel_features.insert(it);
|
|
}
|
|
}
|
|
|
|
void VisualToolDrag::Draw() {
|
|
DrawAllFeatures();
|
|
|
|
// Draw connecting lines
|
|
for (feature_iterator cur = features.begin(); cur != features.end(); ++cur) {
|
|
if (cur->type == DRAG_START) continue;
|
|
|
|
feature_iterator p2 = cur;
|
|
feature_iterator p1 = cur->parent;
|
|
|
|
// Move end marker has an arrow; origin doesn't
|
|
bool has_arrow = p2->type == DRAG_END;
|
|
int arrow_len = has_arrow ? 10 : 0;
|
|
|
|
// Don't show the connecting line if the features are very close
|
|
Vector2D direction = p2->pos - p1->pos;
|
|
if (direction.SquareLen() < (20 + arrow_len) * (20 + arrow_len)) continue;
|
|
|
|
direction = direction.Unit();
|
|
// Get the start and end points of the line
|
|
Vector2D start = p1->pos + direction * 10;
|
|
Vector2D end = p2->pos - direction * (10 + arrow_len);
|
|
|
|
if (has_arrow) {
|
|
gl.SetLineColour(colour[3], 0.8f, 2);
|
|
|
|
// Arrow line
|
|
gl.DrawLine(start, end);
|
|
|
|
// Arrow head
|
|
Vector2D t_half_base_w = Vector2D(-direction.Y(), direction.X()) * 4;
|
|
gl.DrawTriangle(end + direction * arrow_len, end + t_half_base_w, end - t_half_base_w);
|
|
}
|
|
// Draw dashed line
|
|
else {
|
|
gl.SetLineColour(colour[3], 0.5f, 2);
|
|
gl.DrawDashedLine(start, end, 6);
|
|
}
|
|
}
|
|
}
|
|
|
|
void VisualToolDrag::MakeFeatures(AssDialogue *diag) {
|
|
MakeFeatures(diag, features.end());
|
|
}
|
|
|
|
void VisualToolDrag::MakeFeatures(AssDialogue *diag, feature_iterator pos) {
|
|
Vector2D p1 = FromScriptCoords(GetLinePosition(diag));
|
|
|
|
// Create \pos feature
|
|
Feature feat;
|
|
feat.pos = p1;
|
|
feat.layer = 0;
|
|
feat.type = DRAG_START;
|
|
feat.time = 0;
|
|
feat.line = diag;
|
|
feat.parent = features.end();
|
|
features.insert(pos, feat);
|
|
feature_iterator cur = pos; --cur;
|
|
feat.parent = cur;
|
|
if (selection.count(diag))
|
|
sel_features.insert(cur);
|
|
|
|
Vector2D p2;
|
|
int t1, t2;
|
|
|
|
// Create move destination feature
|
|
if (GetLineMove(diag, p1, p2, t1, t2)) {
|
|
feat.pos = FromScriptCoords(p2);
|
|
feat.layer = 1;
|
|
feat.type = DRAG_END;
|
|
feat.parent->time = t1;
|
|
feat.time = t2;
|
|
feat.line = diag;
|
|
features.insert(pos, feat);
|
|
feat.parent->parent = --pos; ++pos;
|
|
}
|
|
|
|
// Create org feature
|
|
if (Vector2D org = GetLineOrigin(diag)) {
|
|
feat.pos = FromScriptCoords(org);
|
|
feat.layer = -1;
|
|
feat.type = DRAG_ORIGIN;
|
|
feat.time = 0;
|
|
feat.line = diag;
|
|
features.insert(pos, feat);
|
|
}
|
|
}
|
|
|
|
bool VisualToolDrag::InitializeDrag(feature_iterator feature) {
|
|
primary = &*feature;
|
|
|
|
// Set time of clicked feature to the current frame and shift all other
|
|
// selected features by the same amount
|
|
if (feature->type != DRAG_ORIGIN) {
|
|
int time = c->videoController->TimeAtFrame(frame_number) - feature->line->Start.GetMS();
|
|
int change = time - feature->time;
|
|
|
|
for (sel_iterator cur = sel_features.begin(); cur != sel_features.end(); ++cur) {
|
|
(*cur)->time += change;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void VisualToolDrag::UpdateDrag(feature_iterator feature) {
|
|
if (feature->type == DRAG_ORIGIN) {
|
|
SetOverride(feature->line, "\\org", ToScriptCoords(feature->pos).PStr());
|
|
return;
|
|
}
|
|
|
|
feature_iterator end_feature = feature->parent;
|
|
if (feature->type == DRAG_END)
|
|
std::swap(feature, end_feature);
|
|
|
|
if (feature->parent == features.end())
|
|
SetOverride(feature->line, "\\pos", ToScriptCoords(feature->pos).PStr());
|
|
else
|
|
SetOverride(feature->line, "\\move",
|
|
wxString::Format("(%s,%s,%d,%d)",
|
|
ToScriptCoords(feature->pos).Str(),
|
|
ToScriptCoords(end_feature->pos).Str(),
|
|
feature->time, end_feature->time));
|
|
}
|
|
|
|
void VisualToolDrag::OnDoubleClick() {
|
|
Vector2D d = ToScriptCoords(mouse_pos) - (primary ? ToScriptCoords(primary->pos) : GetLinePosition(active_line));
|
|
|
|
Selection sel = c->selectionController->GetSelectedSet();
|
|
for (Selection::const_iterator it = sel.begin(); it != sel.end(); ++it) {
|
|
Vector2D p1, p2;
|
|
int t1, t2;
|
|
if (GetLineMove(*it, p1, p2, t1, t2)) {
|
|
if (t1 > 0 || t2 > 0)
|
|
SetOverride(*it, "\\move", wxString::Format("(%s,%s,%d,%d)", (p1 + d).Str(), (p2 + d).Str(), t1, t2));
|
|
else
|
|
SetOverride(*it, "\\move", wxString::Format("(%s,%s)", (p1 + d).Str(), (p2 + d).Str()));
|
|
}
|
|
else
|
|
SetOverride(*it, "\\pos", (GetLinePosition(*it) + d).PStr());
|
|
|
|
if (Vector2D org = GetLineOrigin(*it))
|
|
SetOverride(*it, "\\org", (org + d).PStr());
|
|
}
|
|
|
|
Commit(_("positioning"));
|
|
|
|
OnFileChanged();
|
|
}
|