mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-12-04 22:32:19 +00:00
632 lines
14 KiB
C++
632 lines
14 KiB
C++
/**
|
|
* \file InsetIPAMacro.cpp
|
|
* This file is part of LyX, the document processor.
|
|
* Licence details can be found in the file COPYING.
|
|
*
|
|
* \author Jürgen Spitzmüller
|
|
*
|
|
* Full author contact details are available in file CREDITS.
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include "InsetIPAMacro.h"
|
|
|
|
#include "Buffer.h"
|
|
#include "BufferParams.h"
|
|
#include "Dimension.h"
|
|
#include "Encoding.h"
|
|
#include "FontInfo.h"
|
|
#include "FuncRequest.h"
|
|
#include "FuncStatus.h"
|
|
#include "InsetLayout.h"
|
|
#include "LaTeXFeatures.h"
|
|
#include "Lexer.h"
|
|
#include "MetricsInfo.h"
|
|
#include "xml.h"
|
|
#include "texstream.h"
|
|
|
|
#include "frontends/FontMetrics.h"
|
|
#include "frontends/Painter.h"
|
|
|
|
#include "support/debug.h"
|
|
#include "support/docstream.h"
|
|
#include "support/gettext.h"
|
|
#include "support/Translator.h"
|
|
|
|
using namespace std;
|
|
|
|
namespace lyx {
|
|
|
|
namespace {
|
|
|
|
typedef Translator<string, InsetIPADecoParams::Type> IPADecoTranslator;
|
|
typedef Translator<docstring, InsetIPADecoParams::Type> IPADecoTranslatorLoc;
|
|
|
|
IPADecoTranslator const init_ipadecotranslator()
|
|
{
|
|
IPADecoTranslator translator("toptiebar", InsetIPADecoParams::Toptiebar);
|
|
translator.addPair("bottomtiebar", InsetIPADecoParams::Bottomtiebar);
|
|
return translator;
|
|
}
|
|
|
|
|
|
IPADecoTranslatorLoc const init_ipadecotranslator_loc()
|
|
{
|
|
IPADecoTranslatorLoc translator(_("Top tie bar"), InsetIPADecoParams::Toptiebar);
|
|
translator.addPair(_("Bottom tie bar"), InsetIPADecoParams::Bottomtiebar);
|
|
return translator;
|
|
}
|
|
|
|
|
|
IPADecoTranslator const & ipadecotranslator()
|
|
{
|
|
static IPADecoTranslator const decotranslator =
|
|
init_ipadecotranslator();
|
|
return decotranslator;
|
|
}
|
|
|
|
|
|
IPADecoTranslatorLoc const & ipadecotranslator_loc()
|
|
{
|
|
static IPADecoTranslatorLoc const translator =
|
|
init_ipadecotranslator_loc();
|
|
return translator;
|
|
}
|
|
|
|
|
|
typedef Translator<string, InsetIPAChar::Kind> IPACharTranslator;
|
|
|
|
IPACharTranslator const init_ipachartranslator()
|
|
{
|
|
IPACharTranslator translator("\\tone{51}", InsetIPAChar::TONE_FALLING);
|
|
translator.addPair("\\tone{15}", InsetIPAChar::TONE_RISING);
|
|
translator.addPair("\\tone{45}", InsetIPAChar::TONE_HIGH_RISING);
|
|
translator.addPair("\\tone{12}", InsetIPAChar::TONE_LOW_RISING);
|
|
translator.addPair("\\tone{454}", InsetIPAChar::TONE_HIGH_RISING_FALLING);
|
|
return translator;
|
|
}
|
|
|
|
|
|
IPACharTranslator const & ipachartranslator()
|
|
{
|
|
static IPACharTranslator const chartranslator =
|
|
init_ipachartranslator();
|
|
return chartranslator;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
InsetIPADecoParams::InsetIPADecoParams()
|
|
: type(Bottomtiebar)
|
|
{}
|
|
|
|
|
|
void InsetIPADecoParams::write(ostream & os) const
|
|
{
|
|
string const label = ipadecotranslator().find(type);
|
|
os << "IPADeco " << label << "\n";
|
|
}
|
|
|
|
|
|
void InsetIPADecoParams::read(Lexer & lex)
|
|
{
|
|
string label;
|
|
lex >> label;
|
|
if (lex)
|
|
type = ipadecotranslator().find(label);
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
//
|
|
// InsetIPADeco
|
|
//
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
InsetIPADeco::InsetIPADeco(Buffer * buf, string const & label)
|
|
: InsetCollapsible(buf)
|
|
{
|
|
setDrawFrame(true);
|
|
setFrameColor(Color_insetframe);
|
|
params_.type = ipadecotranslator().find(label);
|
|
}
|
|
|
|
|
|
InsetIPADeco::~InsetIPADeco()
|
|
{}
|
|
|
|
|
|
docstring InsetIPADeco::layoutName() const
|
|
{
|
|
return from_ascii("IPADeco:" + ipadecotranslator().find(params_.type));
|
|
}
|
|
|
|
|
|
void InsetIPADeco::metrics(MetricsInfo & mi, Dimension & dim) const
|
|
{
|
|
InsetText::metrics(mi, dim);
|
|
|
|
if (params_.type == InsetIPADecoParams::Toptiebar) {
|
|
// consider width of the inset label
|
|
FontInfo font(getLayout().labelfont());
|
|
font.realize(sane_font);
|
|
font.decSize();
|
|
font.decSize();
|
|
int w = 0;
|
|
int a = 0;
|
|
int d = 0;
|
|
docstring const label(1, char_type(0x2040));
|
|
theFontMetrics(font).rectText(label, w, a, d);
|
|
dim.asc += int(a * 0.5);
|
|
}
|
|
if (params_.type == InsetIPADecoParams::Bottomtiebar) {
|
|
// consider width of the inset label
|
|
FontInfo font(getLayout().labelfont());
|
|
font.realize(sane_font);
|
|
font.decSize();
|
|
font.decSize();
|
|
int w = 0;
|
|
int a = 0;
|
|
int d = 0;
|
|
docstring const label(1, char_type(0x203f));
|
|
theFontMetrics(font).rectText(label, w, a, d);
|
|
dim.des += int(d * 1.5);
|
|
}
|
|
}
|
|
|
|
|
|
void InsetIPADeco::draw(PainterInfo & pi, int x, int y) const
|
|
{
|
|
// draw the text
|
|
InsetCollapsible::draw(pi, x, y);
|
|
|
|
// draw the inset marker
|
|
drawMarkers(pi, x, y);
|
|
|
|
Dimension const dim = dimension(*pi.base.bv);
|
|
|
|
if (params_.type == InsetIPADecoParams::Toptiebar) {
|
|
FontInfo font(getLayout().labelfont());
|
|
font.realize(sane_font);
|
|
font.decSize();
|
|
font.decSize();
|
|
int w = 0;
|
|
int a = 0;
|
|
int d = 0;
|
|
int asc = dim.ascent();
|
|
docstring const label(1, char_type(0x2040));
|
|
theFontMetrics(font).rectText(label, w, a, d);
|
|
int const ww = max(dim.wid, w);
|
|
pi.pain.rectText(x + (ww - w) / 2, y - int(asc / 2.5),
|
|
label, font, Color_none, Color_none);
|
|
}
|
|
|
|
if (params_.type == InsetIPADecoParams::Bottomtiebar) {
|
|
FontInfo font(getLayout().labelfont());
|
|
font.realize(sane_font);
|
|
font.decSize();
|
|
font.decSize();
|
|
int w = 0;
|
|
int a = 0;
|
|
int d = 0;
|
|
int desc = dim.descent();
|
|
docstring const label(1, char_type(0x203f));
|
|
theFontMetrics(font).rectText(label, w, a, d);
|
|
int const ww = max(dim.wid, w);
|
|
pi.pain.rectText(x + (ww - w) / 2, y + int(desc / 1.5),
|
|
label, font, Color_none, Color_none);
|
|
}
|
|
}
|
|
|
|
|
|
void InsetIPADeco::write(ostream & os) const
|
|
{
|
|
params_.write(os);
|
|
InsetCollapsible::write(os);
|
|
}
|
|
|
|
|
|
void InsetIPADeco::read(Lexer & lex)
|
|
{
|
|
params_.read(lex);
|
|
InsetCollapsible::read(lex);
|
|
}
|
|
|
|
|
|
void InsetIPADeco::doDispatch(Cursor & cur, FuncRequest & cmd)
|
|
{
|
|
switch (cmd.action()) {
|
|
case LFUN_QUOTE_INSERT: {
|
|
FuncRequest fr(LFUN_SELF_INSERT, "\"");
|
|
InsetText::doDispatch(cur, fr);
|
|
break;
|
|
}
|
|
default:
|
|
InsetText::doDispatch(cur, cmd);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
bool InsetIPADeco::getStatus(Cursor & cur, FuncRequest const & cmd,
|
|
FuncStatus & flag) const
|
|
{
|
|
switch (cmd.action()) {
|
|
case LFUN_SCRIPT_INSERT: {
|
|
if (cmd.argument() == "subscript") {
|
|
flag.setEnabled(false);
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
case LFUN_IN_IPA:
|
|
flag.setEnabled(true);
|
|
return true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return InsetText::getStatus(cur, cmd, flag);
|
|
}
|
|
|
|
|
|
void InsetIPADeco::latex(otexstream & os, OutputParams const & runparams) const
|
|
{
|
|
if (params_.type == InsetIPADecoParams::Toptiebar)
|
|
os << "\\texttoptiebar{";
|
|
else if (params_.type == InsetIPADecoParams::Bottomtiebar)
|
|
os << "\\textbottomtiebar{";
|
|
InsetCollapsible::latex(os, runparams);
|
|
os << "}";
|
|
}
|
|
|
|
|
|
namespace {
|
|
std::pair<docstring, docstring> splitPlainTextInHalves(
|
|
const InsetIPADeco * inset, OutputParams const & runparams,
|
|
size_t max_length = INT_MAX)
|
|
{
|
|
odocstringstream ods;
|
|
int h = inset->InsetCollapsible::plaintext(ods, runparams, max_length) / 2;
|
|
docstring result = ods.str();
|
|
docstring const before = result.substr(0, h);
|
|
docstring const after = result.substr(h, result.size());
|
|
return {before, after};
|
|
}
|
|
}
|
|
|
|
|
|
int InsetIPADeco::plaintext(odocstringstream & os,
|
|
OutputParams const & runparams, size_t max_length) const
|
|
{
|
|
docstring before;
|
|
docstring after;
|
|
tie(before, after) = splitPlainTextInHalves(this, runparams, max_length);
|
|
|
|
if (params_.type == InsetIPADecoParams::Toptiebar) {
|
|
os << before;
|
|
os.put(0x0361);
|
|
os << after;
|
|
}
|
|
else if (params_.type == InsetIPADecoParams::Bottomtiebar) {
|
|
os << before;
|
|
os.put(0x035c);
|
|
os << after;
|
|
}
|
|
return before.size() + after.size();
|
|
}
|
|
|
|
|
|
void InsetIPADeco::docbook(XMLStream & xs, OutputParams const & runparams) const
|
|
{
|
|
// The special combining character must be put in the middle, between the two other characters.
|
|
// It will not work if there is anything else than two pure characters, so going back to plaintext.
|
|
docstring before;
|
|
docstring after;
|
|
tie(before, after) = splitPlainTextInHalves(this, runparams);
|
|
|
|
xs << XMLStream::ESCAPE_NONE << before;
|
|
if (params_.type == InsetIPADecoParams::Toptiebar)
|
|
xs << XMLStream::ESCAPE_NONE << "͡";
|
|
else if (params_.type == InsetIPADecoParams::Bottomtiebar)
|
|
xs << XMLStream::ESCAPE_NONE << "͜";
|
|
xs << XMLStream::ESCAPE_NONE << after;
|
|
}
|
|
|
|
|
|
docstring InsetIPADeco::xhtml(XMLStream & xs, OutputParams const & runparams) const
|
|
{
|
|
// The DocBook encoding for this inset has no DocBook tag, but sheer XML (relying on a plaintext
|
|
// transformation of the inset).
|
|
docbook(xs, runparams);
|
|
return docstring();
|
|
}
|
|
|
|
|
|
docstring InsetIPADeco::toolTip(BufferView const &, int, int) const
|
|
{
|
|
return ipadecotranslator_loc().find(params_.type);
|
|
}
|
|
|
|
|
|
string InsetIPADeco::params2string(InsetIPADecoParams const & params)
|
|
{
|
|
ostringstream data;
|
|
data << "IPADeco" << ' ';
|
|
params.write(data);
|
|
return data.str();
|
|
}
|
|
|
|
|
|
void InsetIPADeco::string2params(string const & in, InsetIPADecoParams & params)
|
|
{
|
|
params = InsetIPADecoParams();
|
|
|
|
if (in.empty())
|
|
return;
|
|
|
|
istringstream data(in);
|
|
Lexer lex;
|
|
lex.setStream(data);
|
|
lex.setContext("InsetIPADeco::string2params");
|
|
lex >> "IPADeco" >> "toptiebar";
|
|
|
|
params.read(lex);
|
|
}
|
|
|
|
|
|
void InsetIPADeco::validate(LaTeXFeatures & features) const
|
|
{
|
|
features.require("tipa");
|
|
InsetText::validate(features);
|
|
}
|
|
|
|
|
|
bool InsetIPADeco::insetAllowed(InsetCode code) const
|
|
{
|
|
switch (code) {
|
|
// code that is allowed
|
|
case ERT_CODE:
|
|
case IPACHAR_CODE:
|
|
case SCRIPT_CODE:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// InsetIPAChar
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
InsetIPAChar::InsetIPAChar(Kind k)
|
|
: Inset(nullptr), kind_(k)
|
|
{}
|
|
|
|
|
|
InsetIPAChar::Kind InsetIPAChar::kind() const
|
|
{
|
|
return kind_;
|
|
}
|
|
|
|
|
|
void InsetIPAChar::metrics(MetricsInfo & mi, Dimension & dim) const
|
|
{
|
|
frontend::FontMetrics const & fm =
|
|
theFontMetrics(mi.base.font);
|
|
dim.asc = fm.maxAscent();
|
|
dim.des = fm.maxDescent();
|
|
|
|
string s;
|
|
switch (kind_) {
|
|
case TONE_FALLING:
|
|
case TONE_RISING:
|
|
case TONE_HIGH_RISING:
|
|
case TONE_LOW_RISING:
|
|
case TONE_HIGH_RISING_FALLING:
|
|
s = "_";
|
|
break;
|
|
}
|
|
docstring ds(s.begin(), s.end());
|
|
dim.wid = fm.width(ds);
|
|
}
|
|
|
|
|
|
void InsetIPAChar::draw(PainterInfo & pi, int x, int y) const
|
|
{
|
|
FontInfo font = pi.base.font;
|
|
frontend::FontMetrics const & fm =
|
|
theFontMetrics(font);
|
|
|
|
switch (kind_) {
|
|
case TONE_FALLING:
|
|
{
|
|
int w = fm.width(char_type('-'));
|
|
int h = fm.ascent(char_type('M'));
|
|
int x2 = x + w;
|
|
int y2 = y - h;
|
|
|
|
pi.pain.line(x2, y2, x2, y, Color_foreground);
|
|
pi.pain.line(x2, y, x, y2, Color_foreground);
|
|
break;
|
|
}
|
|
case TONE_RISING:
|
|
{
|
|
int w = fm.width(char_type('-'));
|
|
int h = fm.ascent(char_type('M'));
|
|
int x2 = x + w;
|
|
int y2 = y - h;
|
|
|
|
pi.pain.line(x2, y, x2, y2, Color_foreground);
|
|
pi.pain.line(x2, y2, x, y, Color_foreground);
|
|
break;
|
|
}
|
|
case TONE_HIGH_RISING:
|
|
{
|
|
int w = fm.width(char_type('-'));
|
|
int h = fm.ascent(char_type('M'));
|
|
int x2 = x + w;
|
|
int y2 = y - h;
|
|
int y3 = y - int(h * 0.75);
|
|
|
|
pi.pain.line(x2, y, x2, y2, Color_foreground);
|
|
pi.pain.line(x2, y2, x, y3, Color_foreground);
|
|
break;
|
|
}
|
|
case TONE_LOW_RISING:
|
|
{
|
|
int w = fm.width(char_type('-'));
|
|
int h = fm.ascent(char_type('M'));
|
|
int x2 = x + w;
|
|
int y2 = y - h;
|
|
int y3 = y - int(h * 0.25);
|
|
|
|
pi.pain.line(x2, y, x2, y2, Color_foreground);
|
|
pi.pain.line(x2, y3, x, y, Color_foreground);
|
|
break;
|
|
}
|
|
case TONE_HIGH_RISING_FALLING:
|
|
{
|
|
int w = fm.width(char_type('-'));
|
|
int h = fm.ascent(char_type('M'));
|
|
int x2 = x + w;
|
|
int y2 = y - h;
|
|
int x3 = x + int(w * 0.5);
|
|
int y3 = y - int(h * 0.75);
|
|
|
|
pi.pain.line(x2, y, x2, y2, Color_foreground);
|
|
pi.pain.line(x2, y3, x3, y2, Color_foreground);
|
|
pi.pain.line(x3, y2, x, y3, Color_foreground);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void InsetIPAChar::write(ostream & os) const
|
|
{
|
|
string const command = ipachartranslator().find(kind_);
|
|
if (command.empty()) {
|
|
LYXERR0("InsetIPAChar::write: Unknown type");
|
|
return;
|
|
}
|
|
os << "\\IPAChar " << command << "\n";
|
|
}
|
|
|
|
|
|
void InsetIPAChar::read(Lexer & lex)
|
|
{
|
|
lex.next();
|
|
string const command = lex.getString();
|
|
kind_ = ipachartranslator().find(command);
|
|
}
|
|
|
|
|
|
void InsetIPAChar::latex(otexstream & os,
|
|
OutputParams const &) const
|
|
{
|
|
string const command = ipachartranslator().find(kind_);
|
|
os << command;
|
|
}
|
|
|
|
|
|
int InsetIPAChar::plaintext(odocstringstream & os, OutputParams const &, size_t) const
|
|
{
|
|
switch (kind_) {
|
|
case TONE_FALLING:
|
|
os.put(0x02e5);
|
|
os.put(0x02e9);
|
|
return 2;
|
|
case TONE_RISING:
|
|
os.put(0x02e9);
|
|
os.put(0x02e5);
|
|
return 2;
|
|
case TONE_HIGH_RISING:
|
|
os.put(0x02e7);
|
|
os.put(0x02e5);
|
|
return 2;
|
|
case TONE_LOW_RISING:
|
|
os.put(0x02e9);
|
|
os.put(0x02e7);
|
|
return 2;
|
|
case TONE_HIGH_RISING_FALLING:
|
|
os.put(0x02e8);
|
|
os.put(0x02e5);
|
|
os.put(0x02e8);
|
|
return 3;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
namespace {
|
|
std::string ipaCharToXMLEntity(InsetIPAChar::Kind kind) {
|
|
switch (kind) {
|
|
case InsetIPAChar::Kind::TONE_FALLING:
|
|
return "˥˩";
|
|
case InsetIPAChar::Kind::TONE_RISING:
|
|
return "˩˥";
|
|
case InsetIPAChar::Kind::TONE_HIGH_RISING:
|
|
return "˧˥";
|
|
case InsetIPAChar::Kind::TONE_LOW_RISING:
|
|
return "˩˧";
|
|
case InsetIPAChar::Kind::TONE_HIGH_RISING_FALLING:
|
|
return "˨˥˨";
|
|
}
|
|
return "";
|
|
}
|
|
}
|
|
|
|
|
|
void InsetIPAChar::docbook(XMLStream & xs, OutputParams const &) const
|
|
{
|
|
xs << XMLStream::ESCAPE_NONE << from_ascii(ipaCharToXMLEntity(kind()));
|
|
}
|
|
|
|
|
|
docstring InsetIPAChar::xhtml(XMLStream & xs, OutputParams const &) const
|
|
{
|
|
xs << XMLStream::ESCAPE_NONE << from_ascii(ipaCharToXMLEntity(kind()));
|
|
return docstring();
|
|
}
|
|
|
|
|
|
void InsetIPAChar::toString(odocstream & os) const
|
|
{
|
|
odocstringstream ods;
|
|
plaintext(ods, OutputParams(0));
|
|
os << ods.str();
|
|
}
|
|
|
|
|
|
void InsetIPAChar::forOutliner(docstring & os, size_t const, bool const) const
|
|
{
|
|
odocstringstream ods;
|
|
plaintext(ods, OutputParams(0));
|
|
os += ods.str();
|
|
}
|
|
|
|
|
|
void InsetIPAChar::validate(LaTeXFeatures & features) const
|
|
{
|
|
switch (kind_) {
|
|
case TONE_FALLING:
|
|
case TONE_RISING:
|
|
case TONE_HIGH_RISING:
|
|
case TONE_LOW_RISING:
|
|
case TONE_HIGH_RISING_FALLING:
|
|
features.require("tone");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
} // namespace lyx
|