2011-11-06 18:18:20 +01:00
|
|
|
// Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
|
2007-07-01 05:36:17 +02:00
|
|
|
//
|
2011-11-06 18:18:20 +01:00
|
|
|
// 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.
|
2007-07-01 05:36:17 +02:00
|
|
|
//
|
2011-11-06 18:18:20 +01:00
|
|
|
// 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.
|
2007-07-01 05:36:17 +02:00
|
|
|
//
|
2009-07-29 07:43:02 +02:00
|
|
|
// Aegisub Project http://www.aegisub.org/
|
2007-07-01 05:36:17 +02:00
|
|
|
//
|
2009-07-29 07:43:02 +02:00
|
|
|
// $Id$
|
|
|
|
|
|
|
|
/// @file visual_tool_drag.cpp
|
|
|
|
/// @brief Position all visible subtitles by dragging visual typesetting tool
|
|
|
|
/// @ingroup visual_ts
|
2007-07-01 05:36:17 +02:00
|
|
|
|
2009-01-04 07:31:48 +01:00
|
|
|
#include "config.h"
|
|
|
|
|
2011-11-06 18:18:20 +01:00
|
|
|
#include "visual_tool_drag.h"
|
|
|
|
|
|
|
|
#ifndef AGI_PRE
|
|
|
|
#include <wx/bmpbuttn.h>
|
|
|
|
#include <wx/toolbar.h>
|
|
|
|
#endif
|
|
|
|
|
2007-07-01 05:36:17 +02:00
|
|
|
#include "ass_dialogue.h"
|
2009-09-10 15:06:40 +02:00
|
|
|
#include "ass_file.h"
|
2011-01-16 08:17:36 +01:00
|
|
|
#include "include/aegisub/context.h"
|
2009-09-10 15:06:40 +02:00
|
|
|
#include "libresrc/libresrc.h"
|
2007-07-01 05:36:17 +02:00
|
|
|
#include "utils.h"
|
2009-09-10 03:41:34 +02:00
|
|
|
#include "video_context.h"
|
2007-07-01 05:36:17 +02:00
|
|
|
|
2010-05-23 10:53:27 +02:00
|
|
|
static const DraggableFeatureType DRAG_ORIGIN = DRAG_BIG_TRIANGLE;
|
|
|
|
static const DraggableFeatureType DRAG_START = DRAG_BIG_SQUARE;
|
|
|
|
static const DraggableFeatureType DRAG_END = DRAG_BIG_CIRCLE;
|
2007-07-04 06:24:47 +02:00
|
|
|
|
2011-11-06 18:18:20 +01:00
|
|
|
VisualToolDrag::VisualToolDrag(VideoDisplay *parent, agi::Context *context)
|
|
|
|
: VisualTool<VisualToolDragDraggableFeature>(parent, context)
|
|
|
|
, primary(0)
|
|
|
|
, button_is_move(true)
|
2007-07-01 05:36:17 +02:00
|
|
|
{
|
2011-01-16 08:17:36 +01:00
|
|
|
c->selectionController->GetSelectedSet(selection);
|
2011-11-06 18:18:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2007-07-04 06:24:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VisualToolDrag::UpdateToggleButtons() {
|
2011-11-06 18:18:20 +01:00
|
|
|
bool to_move = true;
|
|
|
|
if (active_line) {
|
|
|
|
Vector2D p1, p2;
|
|
|
|
int t1, t2;
|
|
|
|
to_move = !GetLineMove(active_line, p1, p2, t1, t2);
|
2007-07-04 06:24:47 +02:00
|
|
|
}
|
|
|
|
|
2011-11-06 18:18:20 +01:00
|
|
|
if (to_move == button_is_move) return;
|
2007-07-04 06:24:47 +02:00
|
|
|
|
2011-11-06 18:18:20 +01:00
|
|
|
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;
|
2007-07-04 06:24:47 +02:00
|
|
|
}
|
|
|
|
|
2010-05-26 09:17:46 +02:00
|
|
|
void VisualToolDrag::OnSubTool(wxCommandEvent &) {
|
2007-07-04 06:24:47 +02:00
|
|
|
// Toggle \move <-> \pos
|
2011-11-06 18:18:20 +01:00
|
|
|
VideoContext *vc = c->videoController;
|
2010-06-30 08:29:14 +02:00
|
|
|
for (Selection::const_iterator cur = selection.begin(); cur != selection.end(); ++cur) {
|
2010-06-28 09:39:36 +02:00
|
|
|
AssDialogue *line = *cur;
|
2011-11-06 18:18:20 +01:00
|
|
|
Vector2D p1, p2;
|
|
|
|
int t1, t2;
|
2010-05-26 09:17:46 +02:00
|
|
|
|
2011-11-06 18:18:20 +01:00
|
|
|
bool has_move = GetLineMove(line, p1, p2, t1, t2);
|
2007-07-04 06:24:47 +02:00
|
|
|
|
2011-11-06 18:18:20 +01:00
|
|
|
if (has_move)
|
|
|
|
SetOverride(line, "\\pos", p1.PStr());
|
|
|
|
else {
|
|
|
|
p1 = GetLinePosition(line);
|
|
|
|
// Round the start and end times to exact frames
|
2011-12-22 22:28:51 +01:00
|
|
|
int start = vc->TimeAtFrame(vc->FrameAtTime(line->Start, agi::vfr::START)) - line->Start;
|
|
|
|
int end = vc->TimeAtFrame(vc->FrameAtTime(line->Start, agi::vfr::END)) - line->Start;
|
2011-11-06 18:18:20 +01:00
|
|
|
SetOverride(line, "\\move", wxString::Format("(%s,%s,%d,%d)", p1.Str(), p1.Str(), start, end));
|
|
|
|
}
|
2007-07-04 06:24:47 +02:00
|
|
|
}
|
2010-05-26 09:17:46 +02:00
|
|
|
|
2010-10-26 06:12:10 +02:00
|
|
|
Commit();
|
2011-11-06 18:18:20 +01:00
|
|
|
OnFileChanged();
|
2010-10-16 22:23:54 +02:00
|
|
|
UpdateToggleButtons();
|
2007-07-04 06:24:47 +02:00
|
|
|
}
|
|
|
|
|
2010-06-28 09:13:15 +02:00
|
|
|
void VisualToolDrag::OnLineChanged() {
|
2007-07-04 06:24:47 +02:00
|
|
|
UpdateToggleButtons();
|
2007-07-01 05:36:17 +02:00
|
|
|
}
|
|
|
|
|
2010-06-28 09:13:15 +02:00
|
|
|
void VisualToolDrag::OnFileChanged() {
|
2010-06-30 08:29:14 +02:00
|
|
|
/// @todo it should be possible to preserve the selection in some cases
|
|
|
|
features.clear();
|
2011-11-06 18:18:20 +01:00
|
|
|
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))
|
2011-12-27 02:38:00 +01:00
|
|
|
MakeFeatures(diag);
|
2010-06-30 08:29:14 +02:00
|
|
|
}
|
2011-11-06 18:18:20 +01:00
|
|
|
|
2010-10-16 22:23:54 +02:00
|
|
|
UpdateToggleButtons();
|
2010-06-28 09:13:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void VisualToolDrag::OnFrameChanged() {
|
2011-11-06 18:18:20 +01:00
|
|
|
if (primary && !IsDisplayed(primary->line))
|
|
|
|
primary = 0;
|
2010-06-30 08:29:14 +02:00
|
|
|
|
|
|
|
feature_iterator feat = features.begin();
|
|
|
|
feature_iterator end = features.end();
|
|
|
|
|
2011-11-06 18:18:20 +01:00
|
|
|
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)) {
|
2010-06-30 08:29:14 +02:00
|
|
|
// Features don't exist and should
|
2011-11-06 18:18:20 +01:00
|
|
|
if (feat == end || feat->line != diag)
|
2010-06-30 08:29:14 +02:00
|
|
|
MakeFeatures(diag, feat);
|
|
|
|
// Move past already existing features for the line
|
2011-11-06 18:18:20 +01:00
|
|
|
else
|
2010-06-30 08:29:14 +02:00
|
|
|
while (feat != end && feat->line == diag) ++feat;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Remove all features for this line (if any)
|
|
|
|
while (feat != end && feat->line == diag) {
|
2011-12-27 02:38:00 +01:00
|
|
|
if (feat == active_feature) active_feature = features.end();
|
2011-11-06 18:18:20 +01:00
|
|
|
feat->line = 0;
|
2010-06-30 08:29:14 +02:00
|
|
|
RemoveSelection(feat);
|
|
|
|
feat = features.erase(feat);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-06-28 09:13:15 +02:00
|
|
|
}
|
|
|
|
|
Remove the SelectionChangeSubscriber mechanism from the grid and implement some basic selection change notification through SelectionController.
Change SelectionListener interface so it receives the set of lines added and removed from selection, instead of just the complete new selection.
Update VisualTool<> to use SelectionListener to receive selection change notifications.
This change (temporarily, I hope) breaks feature selection in visual drag mode, when changing selection via the grid. This is caused by the grid selection change first clearing the entire selection, which sends a separate notification about selection clear. This causes the last visual feature to be deselected, and then the visual tool base reselects the active line, causing a new notification for selection to be sent. The active line happens to be the newly clicked line, and the selection notification enters during the externalChange guard being set, and is then ignored for feature update purposes. When control returns to the original SelectRow call in the grid, the line to be selected has already been selected and then nothing happens.
The best fix is to avoid two notifications being required to deselect all then reselect one line in the first place, so making the grid selection handling saner is the best fix.
Originally committed to SVN as r4602.
2010-06-26 06:38:02 +02:00
|
|
|
void VisualToolDrag::OnSelectedSetChanged(const Selection &added, const Selection &removed) {
|
2011-01-16 08:17:36 +01:00
|
|
|
c->selectionController->GetSelectedSet(selection);
|
Remove the SelectionChangeSubscriber mechanism from the grid and implement some basic selection change notification through SelectionController.
Change SelectionListener interface so it receives the set of lines added and removed from selection, instead of just the complete new selection.
Update VisualTool<> to use SelectionListener to receive selection change notifications.
This change (temporarily, I hope) breaks feature selection in visual drag mode, when changing selection via the grid. This is caused by the grid selection change first clearing the entire selection, which sends a separate notification about selection clear. This causes the last visual feature to be deselected, and then the visual tool base reselects the active line, causing a new notification for selection to be sent. The active line happens to be the newly clicked line, and the selection notification enters during the externalChange guard being set, and is then ignored for feature update purposes. When control returns to the original SelectRow call in the grid, the line to be selected has already been selected and then nothing happens.
The best fix is to avoid two notifications being required to deselect all then reselect one line in the first place, so making the grid selection handling saner is the best fix.
Originally committed to SVN as r4602.
2010-06-26 06:38:02 +02:00
|
|
|
|
2011-11-06 18:18:20 +01:00
|
|
|
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);
|
2010-05-26 09:17:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-07-01 05:36:17 +02:00
|
|
|
void VisualToolDrag::Draw() {
|
2007-07-01 09:09:37 +02:00
|
|
|
DrawAllFeatures();
|
2007-07-04 02:36:04 +02:00
|
|
|
|
2011-11-06 18:18:20 +01:00
|
|
|
// Draw connecting lines
|
2010-05-26 09:17:39 +02:00
|
|
|
for (feature_iterator cur = features.begin(); cur != features.end(); ++cur) {
|
2010-05-23 10:53:27 +02:00
|
|
|
if (cur->type == DRAG_START) continue;
|
2011-11-06 18:18:20 +01:00
|
|
|
|
2010-06-30 08:29:14 +02:00
|
|
|
feature_iterator p2 = cur;
|
|
|
|
feature_iterator p1 = cur->parent;
|
2010-05-20 10:55:52 +02:00
|
|
|
|
2011-11-06 18:18:20 +01:00
|
|
|
// 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);
|
2010-05-20 10:55:52 +02:00
|
|
|
|
|
|
|
// Arrow line
|
2011-11-06 18:18:20 +01:00
|
|
|
gl.DrawLine(start, end);
|
2010-05-20 10:55:52 +02:00
|
|
|
|
|
|
|
// Arrow head
|
2011-11-06 18:18:20 +01:00
|
|
|
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);
|
2010-05-20 10:55:52 +02:00
|
|
|
}
|
|
|
|
// Draw dashed line
|
|
|
|
else {
|
2011-11-06 18:18:20 +01:00
|
|
|
gl.SetLineColour(colour[3], 0.5f, 2);
|
|
|
|
gl.DrawDashedLine(start, end, 6);
|
2007-07-04 02:36:04 +02:00
|
|
|
}
|
|
|
|
}
|
2007-07-01 05:36:17 +02:00
|
|
|
}
|
2011-11-06 18:18:20 +01:00
|
|
|
|
2010-06-30 08:29:14 +02:00
|
|
|
void VisualToolDrag::MakeFeatures(AssDialogue *diag) {
|
|
|
|
MakeFeatures(diag, features.end());
|
2010-05-26 09:17:39 +02:00
|
|
|
}
|
2011-11-06 18:18:20 +01:00
|
|
|
|
2010-06-30 08:29:14 +02:00
|
|
|
void VisualToolDrag::MakeFeatures(AssDialogue *diag, feature_iterator pos) {
|
2011-11-06 18:18:20 +01:00
|
|
|
Vector2D p1 = FromScriptCoords(GetLinePosition(diag));
|
2010-06-30 08:29:14 +02:00
|
|
|
|
|
|
|
// Create \pos feature
|
|
|
|
Feature feat;
|
2011-11-06 18:18:20 +01:00
|
|
|
feat.pos = p1;
|
2010-06-30 08:29:14 +02:00
|
|
|
feat.layer = 0;
|
|
|
|
feat.type = DRAG_START;
|
2011-11-06 18:18:20 +01:00
|
|
|
feat.time = 0;
|
2010-06-30 08:29:14 +02:00
|
|
|
feat.line = diag;
|
|
|
|
feat.parent = features.end();
|
|
|
|
features.insert(pos, feat);
|
|
|
|
feature_iterator cur = pos; --cur;
|
|
|
|
feat.parent = cur;
|
2011-11-06 18:18:20 +01:00
|
|
|
if (selection.count(diag))
|
|
|
|
sel_features.insert(cur);
|
|
|
|
|
|
|
|
Vector2D p2;
|
|
|
|
int t1, t2;
|
2007-07-01 09:09:37 +02:00
|
|
|
|
2010-06-30 08:29:14 +02:00
|
|
|
// Create move destination feature
|
2011-11-06 18:18:20 +01:00
|
|
|
if (GetLineMove(diag, p1, p2, t1, t2)) {
|
|
|
|
feat.pos = FromScriptCoords(p2);
|
2010-06-30 08:29:14 +02:00
|
|
|
feat.layer = 1;
|
|
|
|
feat.type = DRAG_END;
|
2011-11-06 18:18:20 +01:00
|
|
|
feat.parent->time = t1;
|
2010-06-30 08:29:14 +02:00
|
|
|
feat.time = t2;
|
|
|
|
feat.line = diag;
|
|
|
|
features.insert(pos, feat);
|
|
|
|
feat.parent->parent = --pos; ++pos;
|
|
|
|
}
|
2011-11-06 18:18:20 +01:00
|
|
|
|
2010-06-30 08:29:14 +02:00
|
|
|
// Create org feature
|
2011-11-06 18:18:20 +01:00
|
|
|
if (Vector2D org = GetLineOrigin(diag)) {
|
|
|
|
feat.pos = FromScriptCoords(org);
|
2010-06-30 08:29:14 +02:00
|
|
|
feat.layer = -1;
|
|
|
|
feat.type = DRAG_ORIGIN;
|
|
|
|
feat.time = 0;
|
|
|
|
feat.line = diag;
|
|
|
|
features.insert(pos, feat);
|
|
|
|
}
|
|
|
|
}
|
2007-07-01 09:09:37 +02:00
|
|
|
|
2010-06-30 08:29:14 +02:00
|
|
|
bool VisualToolDrag::InitializeDrag(feature_iterator feature) {
|
|
|
|
primary = &*feature;
|
2010-05-26 09:17:39 +02:00
|
|
|
|
2010-06-30 08:29:14 +02:00
|
|
|
// Set time of clicked feature to the current frame and shift all other
|
|
|
|
// selected features by the same amount
|
|
|
|
if (feature->type != DRAG_ORIGIN) {
|
2011-12-22 22:28:51 +01:00
|
|
|
int time = c->videoController->TimeAtFrame(frame_number) - feature->line->Start;
|
2010-06-30 08:29:14 +02:00
|
|
|
int change = time - feature->time;
|
2010-05-26 09:17:39 +02:00
|
|
|
|
2011-11-06 18:18:20 +01:00
|
|
|
for (sel_iterator cur = sel_features.begin(); cur != sel_features.end(); ++cur) {
|
|
|
|
(*cur)->time += change;
|
2007-07-01 09:09:37 +02:00
|
|
|
}
|
|
|
|
}
|
2010-05-23 10:53:27 +02:00
|
|
|
return true;
|
|
|
|
}
|
2007-07-01 09:09:37 +02:00
|
|
|
|
2011-11-06 18:18:20 +01:00
|
|
|
void VisualToolDrag::UpdateDrag(feature_iterator feature) {
|
2010-05-23 10:53:27 +02:00
|
|
|
if (feature->type == DRAG_ORIGIN) {
|
2011-11-06 18:18:20 +01:00
|
|
|
SetOverride(feature->line, "\\org", ToScriptCoords(feature->pos).PStr());
|
2010-05-20 10:55:52 +02:00
|
|
|
return;
|
2007-07-04 09:26:24 +02:00
|
|
|
}
|
|
|
|
|
2011-11-06 18:18:20 +01:00
|
|
|
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));
|
2007-07-01 05:36:17 +02:00
|
|
|
}
|
2010-05-23 10:53:27 +02:00
|
|
|
|
2011-11-06 18:18:20 +01:00
|
|
|
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));
|
2010-05-23 10:53:27 +02:00
|
|
|
else
|
2011-11-06 18:18:20 +01:00
|
|
|
SetOverride(*it, "\\move", wxString::Format("(%s,%s)", (p1 + d).Str(), (p2 + d).Str()));
|
2010-05-26 09:17:28 +02:00
|
|
|
}
|
2011-11-06 18:18:20 +01:00
|
|
|
else
|
|
|
|
SetOverride(*it, "\\pos", (GetLinePosition(*it) + d).PStr());
|
|
|
|
|
|
|
|
if (Vector2D org = GetLineOrigin(*it))
|
|
|
|
SetOverride(*it, "\\org", (org + d).PStr());
|
2010-05-23 10:53:27 +02:00
|
|
|
}
|
|
|
|
|
2010-10-26 06:12:10 +02:00
|
|
|
Commit(_("positioning"));
|
2010-05-23 10:53:27 +02:00
|
|
|
|
2010-06-30 08:29:14 +02:00
|
|
|
OnFileChanged();
|
2010-05-23 10:53:27 +02:00
|
|
|
}
|