Aegisub/libaegisub/common/calltip_provider.cpp

185 lines
5.4 KiB
C++

// Copyright (c) 2012, 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/
#include "libaegisub/calltip_provider.h"
#include "libaegisub/ass/dialogue_parser.h"
#include <boost/range.hpp>
namespace {
struct proto_lit {
const char *name;
bool has_parens;
const char *args;
};
proto_lit calltip_protos[] = {
{ "move", true, "X1\0Y1\0X2\0Y2\0" },
{ "move", true, "X1\0Y1\0X2\0Y2\0Start Time\0End Time\0" },
{ "fn", false, "Font Name\0" },
{ "bord", false, "Width\0" },
{ "xbord", false, "Width\0" },
{ "ybord", false, "Width\0" },
{ "shad", false, "Depth\0" },
{ "xshad", false, "Depth\0" },
{ "yshad", false, "Depth\0" },
{ "be", false, "Strength\0" },
{ "blur", false, "Strength\0" },
{ "fscx", false, "Scale\0" },
{ "fscy", false, "Scale\0" },
{ "fsp", false, "Spacing\0" },
{ "fs", false, "Font Size\0" },
{ "fe", false, "Encoding\0" },
{ "frx", false, "Angle\0" },
{ "fry", false, "Angle\0" },
{ "frz", false, "Angle\0" },
{ "fr", false, "Angle\0" },
{ "pbo", false, "Offset\0" },
{ "clip", true, "Command\0" },
{ "clip", true, "Scale\0Command\0" },
{ "clip", true, "X1\0Y1\0X2\0Y2\0" },
{ "iclip", true, "Command\0" },
{ "iclip", true, "Scale\0Command\0" },
{ "iclip", true, "X1\0Y1\0X2\0Y2\0" },
{ "t", true, "Acceleration\0Tags\0" },
{ "t", true, "Start Time\0End Time\0Tags\0" },
{ "t", true, "Start Time\0End Time\0Acceleration\0Tags\0" },
{ "pos", true, "X\0Y\0" },
{ "p", false, "Exponent\0" },
{ "org", true, "X\0Y\0" },
{ "fade", true, "Start Alpha\0Middle Alpha\0End Alpha\0Start In\0End In\0Start Out\0End Out\0" },
{ "fad", true, "Start Time\0End Time\0" },
{ "c", false, "Colour\0" },
{ "1c", false, "Colour\0" },
{ "2c", false, "Colour\0" },
{ "3c", false, "Colour\0" },
{ "4c", false, "Colour\0" },
{ "alpha", false, "Alpha\0" },
{ "1a", false, "Alpha\0" },
{ "2a", false, "Alpha\0" },
{ "3a", false, "Alpha\0" },
{ "4a", false, "Alpha\0" },
{ "an", false, "Alignment\0" },
{ "a", false, "Alignment\0" },
{ "b", false, "Weight\0" },
{ "i", false, "1/0\0" },
{ "u", false, "1/0\0" },
{ "s", false, "1/0\0" },
{ "kf", false, "Duration\0" },
{ "ko", false, "Duration\0" },
{ "k", false, "Duration\0" },
{ "K", false, "Duration\0" },
{ "q", false, "Wrap Style\0" },
{ "r", false, "Style\0" },
{ "fax", false, "Factor\0" },
{ "fay", false, "Factor\0" }
};
}
namespace agi {
CalltipProvider::CalltipProvider() {
for (auto proto : calltip_protos) {
CalltipProto p;
std::string tag_name = proto.name;
p.text = '\\' + tag_name;
if (proto.has_parens)
p.text += '(';
for (const char *arg = proto.args; *arg; ) {
size_t start = p.text.size();
p.text += arg;
size_t end = p.text.size();
if (proto.has_parens)
p.text += ',';
arg += end - start + 1;
p.args.emplace_back(start, end);
}
// replace trailing comma
if (proto.has_parens)
p.text.back() = ')';
protos.insert(make_pair(tag_name, p));
}
}
Calltip CalltipProvider::GetCalltip(std::vector<ass::DialogueToken> const& tokens, std::string const& text, size_t pos) {
namespace dt = ass::DialogueTokenType;
Calltip ret = { "", 0, 0, 0 };
size_t idx = 0;
size_t tag_name_idx = 0;
size_t commas = 0;
for (; idx < tokens.size() && pos >= tokens[idx].length; ++idx) {
switch (tokens[idx].type) {
case dt::COMMENT:
case dt::OVR_END:
tag_name_idx = 0;
break;
case dt::TAG_NAME:
tag_name_idx = idx;
commas = 0;
break;
case dt::ARG_SEP:
++commas;
break;
default: break;
}
pos -= tokens[idx].length;
}
// Either didn't hit a tag or the override block ended before reaching the
// current position
if (tag_name_idx == 0)
return ret;
// Find the prototype for this tag
size_t tag_name_start = 0;
for (size_t i = 0; i < tag_name_idx; ++i)
tag_name_start += tokens[i].length;
auto it = protos.equal_range(text.substr(tag_name_start, tokens[tag_name_idx].length));
// If there's multiple overloads, check how many total arguments we have
// and pick the one with the least args >= current arg count
if (distance(it.first, it.second) > 1) {
size_t args = commas + 1;
for (size_t i = idx; i < tokens.size(); ++i) {
int type = tokens[i].type;
if (type == dt::ARG_SEP)
++args;
else if (type != dt::ARG && type != dt::WHITESPACE)
break;
}
while (it.first != it.second && args > it.first->second.args.size())
++it.first;
}
// Unknown tag or too many arguments
if (it.first == it.second || it.first->second.args.size() <= commas)
return ret;
ret.text = it.first->second.text;
ret.highlight_start = it.first->second.args[commas].first;
ret.highlight_end = it.first->second.args[commas].second;
ret.tag_position = tag_name_start;
return ret;
}
}