lyx_mirror/src/insets/InsetCommandParams.cpp
Georg Baum 1a6c599917 Extend the notermination flag to math as well.
Math commands need it as well as text commands. At the same time, this
further unifies the checking for termination and fixes cases of wrong
output (e.g. for 0x2005).
2012-03-25 15:57:38 +02:00

514 lines
12 KiB
C++

/**
* \file InsetCommandParams.cpp
* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
* \author Angus Leeming
* \author Georg Baum
* \author Richard Heck
*
* Full author contact details are available in file CREDITS.
*/
#include <config.h>
#include <algorithm>
#include "InsetCommandParams.h"
#include "InsetBibitem.h"
#include "InsetBibtex.h"
#include "InsetCitation.h"
#include "InsetFloatList.h"
#include "InsetHyperlink.h"
#include "InsetInclude.h"
#include "InsetIndex.h"
#include "InsetLabel.h"
#include "InsetLine.h"
#include "InsetNomencl.h"
#include "InsetRef.h"
#include "InsetTOC.h"
#include "Encoding.h"
#include "Lexer.h"
#include "OutputParams.h"
#include "frontends/alert.h"
#include "support/debug.h"
#include "support/docstream.h"
#include "support/ExceptionMessage.h"
#include "support/gettext.h"
#include "support/lstrings.h"
#include "support/lassert.h"
using namespace std;
using namespace lyx::support;
namespace lyx {
/// Get information for \p code and command \p cmdName.
/// Returns 0 if the combination is not known. [FIXME: 0?]
/// Don't call this without first making sure the command name is
/// acceptable to the inset.
static ParamInfo const & findInfo(InsetCode code, string const & cmdName)
{
switch (code) {
case BIBITEM_CODE:
return InsetBibitem::findInfo(cmdName);
case BIBTEX_CODE:
return InsetBibtex::findInfo(cmdName);
case CITE_CODE:
return InsetCitation::findInfo(cmdName);
case FLOAT_LIST_CODE:
return InsetFloatList::findInfo(cmdName);
case HYPERLINK_CODE:
return InsetHyperlink::findInfo(cmdName);
case INCLUDE_CODE:
return InsetInclude::findInfo(cmdName);
case INDEX_PRINT_CODE:
return InsetPrintIndex::findInfo(cmdName);
case LABEL_CODE:
return InsetLabel::findInfo(cmdName);
case LINE_CODE:
return InsetLine::findInfo(cmdName);
case NOMENCL_CODE:
return InsetNomencl::findInfo(cmdName);
case NOMENCL_PRINT_CODE:
return InsetPrintNomencl::findInfo(cmdName);
case REF_CODE:
return InsetRef::findInfo(cmdName);
case TOC_CODE:
return InsetTOC::findInfo(cmdName);
default:
LASSERT(false, /**/);
}
static ParamInfo pi;
return pi; // to silence the warning
}
/////////////////////////////////////////////////////////////////////
//
// ParamInfo::ParamData
//
/////////////////////////////////////////////////////////////////////
ParamInfo::ParamData::ParamData(std::string const & s, ParamType t,
ParamHandling h)
: name_(s), type_(t), handling_(h)
{}
bool ParamInfo::ParamData::isOptional() const
{
return type_ == ParamInfo::LATEX_OPTIONAL;
}
bool ParamInfo::ParamData::operator==(ParamInfo::ParamData const & rhs) const
{
return name() == rhs.name() && type() == rhs.type()
&& handling() == rhs.handling();
}
bool ParamInfo::hasParam(std::string const & name) const
{
const_iterator it = begin();
const_iterator last = end();
for (; it != last; ++it) {
if (it->name() == name)
return true;
}
return false;
}
void ParamInfo::add(std::string const & name, ParamType type,
ParamHandling handling)
{
info_.push_back(ParamData(name, type, handling));
}
bool ParamInfo::operator==(ParamInfo const & rhs) const
{
if (size() != rhs.size())
return false;
return equal(begin(), end(), rhs.begin());
}
ParamInfo::ParamData const &
ParamInfo::operator[](std::string const & name) const
{
LASSERT(hasParam(name), /**/);
const_iterator it = begin();
const_iterator last = end();
for (; it != last; ++it) {
if (it->name() == name)
return *it;
}
return *it; // silence warning
}
/////////////////////////////////////////////////////////////////////
//
// InsetCommandParams
//
/////////////////////////////////////////////////////////////////////
InsetCommandParams::InsetCommandParams(InsetCode code)
: insetCode_(code), preview_(false)
{
cmdName_ = getDefaultCmd(code);
info_ = findInfo(code, cmdName_);
}
InsetCommandParams::InsetCommandParams(InsetCode code,
string const & cmdName)
: insetCode_(code), cmdName_(cmdName), preview_(false)
{
info_ = findInfo(code, cmdName);
}
std::string InsetCommandParams::insetType() const
{
return insetName(insetCode_);
}
string InsetCommandParams::getDefaultCmd(InsetCode code)
{
switch (code) {
case BIBITEM_CODE:
return InsetBibitem::defaultCommand();
case BIBTEX_CODE:
return InsetBibtex::defaultCommand();
case CITE_CODE:
return InsetCitation::defaultCommand();
case FLOAT_LIST_CODE:
return InsetFloatList::defaultCommand();
case HYPERLINK_CODE:
return InsetHyperlink::defaultCommand();
case INCLUDE_CODE:
return InsetInclude::defaultCommand();
case INDEX_PRINT_CODE:
return InsetPrintIndex::defaultCommand();
case LABEL_CODE:
return InsetLabel::defaultCommand();
case LINE_CODE:
return InsetLine::defaultCommand();
case NOMENCL_CODE:
return InsetNomencl::defaultCommand();
case NOMENCL_PRINT_CODE:
return InsetPrintNomencl::defaultCommand();
case REF_CODE:
return InsetRef::defaultCommand();
case TOC_CODE:
return InsetTOC::defaultCommand();
default:
LASSERT(false, /**/);
}
return string(); // silence the warning
}
bool InsetCommandParams::isCompatibleCommand(InsetCode code, string const & s)
{
switch (code) {
case BIBITEM_CODE:
return InsetBibitem::isCompatibleCommand(s);
case BIBTEX_CODE:
return InsetBibtex::isCompatibleCommand(s);
case CITE_CODE:
return InsetCitation::isCompatibleCommand(s);
case FLOAT_LIST_CODE:
return InsetFloatList::isCompatibleCommand(s);
case HYPERLINK_CODE:
return InsetHyperlink::isCompatibleCommand(s);
case INCLUDE_CODE:
return InsetInclude::isCompatibleCommand(s);
case INDEX_PRINT_CODE:
return InsetPrintIndex::isCompatibleCommand(s);
case LABEL_CODE:
return InsetLabel::isCompatibleCommand(s);
case LINE_CODE:
return InsetLine::isCompatibleCommand(s);
case NOMENCL_CODE:
return InsetNomencl::isCompatibleCommand(s);
case NOMENCL_PRINT_CODE:
return InsetPrintNomencl::isCompatibleCommand(s);
case REF_CODE:
return InsetRef::isCompatibleCommand(s);
case TOC_CODE:
return InsetTOC::isCompatibleCommand(s);
default:
LASSERT(false, /**/);
}
return false; // silence the warning
}
void InsetCommandParams::setCmdName(string const & name)
{
if (!isCompatibleCommand(insetCode_, name)) {
LYXERR0("InsetCommand: Incompatible command name " <<
name << ".");
throw ExceptionMessage(WarningException, _("InsetCommand Error: "),
_("Incompatible command name."));
}
cmdName_ = name;
info_ = findInfo(insetCode_, cmdName_);
}
void InsetCommandParams::read(Lexer & lex)
{
lex.setContext("InsetCommandParams::read");
lex >> insetName(insetCode_).c_str();
lex >> "LatexCommand";
lex >> cmdName_;
if (!isCompatibleCommand(insetCode_, cmdName_)) {
lex.printError("Incompatible command name " + cmdName_ + ".");
throw ExceptionMessage(WarningException, _("InsetCommandParams Error: "),
_("Incompatible command name."));
}
info_ = findInfo(insetCode_, cmdName_);
string token;
while (lex.isOK()) {
lex.next();
token = lex.getString();
if (token == "\\end_inset")
break;
if (token == "preview") {
lex.next();
preview_ = lex.getBool();
continue;
}
if (info_.hasParam(token)) {
lex.next(true);
params_[token] = lex.getDocString();
} else {
lex.printError("Unknown parameter name `$$Token' for command " + cmdName_);
throw ExceptionMessage(WarningException,
_("InsetCommandParams: ") + from_ascii(cmdName_),
_("Unknown parameter name: ") + from_utf8(token));
}
}
if (token != "\\end_inset") {
lex.printError("Missing \\end_inset at this point. "
"Read: `$$Token'");
throw ExceptionMessage(WarningException,
_("InsetCommandParams Error: "),
_("Missing \\end_inset at this point: ") + from_utf8(token));
}
}
void InsetCommandParams::write(ostream & os) const
{
os << "CommandInset " << insetType() << '\n';
os << "LatexCommand " << cmdName_ << '\n';
if (preview_)
os << "preview true\n";
ParamInfo::const_iterator it = info_.begin();
ParamInfo::const_iterator end = info_.end();
for (; it != end; ++it) {
std::string const & name = it->name();
docstring const & data = (*this)[name];
if (!data.empty()) {
// FIXME UNICODE
os << name << ' '
<< Lexer::quoteString(to_utf8(data))
<< '\n';
}
}
}
bool InsetCommandParams::writeEmptyOptional(ParamInfo::const_iterator ci) const
{
if (!ci->isOptional()) {
LASSERT(false, /**/);
}
++ci; // we want to start with the next one
ParamInfo::const_iterator end = info_.end();
for (; ci != end; ++ci) {
switch (ci->type()) {
case ParamInfo::LYX_INTERNAL:
break;
case ParamInfo::LATEX_REQUIRED:
return false;
case ParamInfo::LATEX_OPTIONAL: {
std::string const & name = ci->name();
docstring const & data = (*this)[name];
if (!data.empty())
return true;
break;
}
} //end switch
}
return false;
}
docstring InsetCommandParams::prepareCommand(OutputParams const & runparams,
docstring const & command,
ParamInfo::ParamHandling handling) const
{
docstring result;
switch (handling) {
case ParamInfo::HANDLING_LATEXIFY: {
docstring uncodable;
for (size_t n = 0; n < command.size(); ++n) {
try {
char_type const c = command[n];
docstring const latex = runparams.encoding->latexChar(c).first;
result += latex;
if (latex.length() > 1 && latex[latex.length() - 1] != '}') {
// Prevent eating of a following
// space or command corruption by
// following characters
result += "{}";
}
} catch (EncodingException & /* e */) {
LYXERR0("Uncodable character in command inset!");
if (runparams.dryrun) {
result += "<" + _("LyX Warning: ")
+ _("uncodable character") + " '";
result += docstring(1, command[n]);
result += "'>";
} else
uncodable += command[n];
}
}
if (!uncodable.empty()) {
// issue a warning about omitted characters
// FIXME: should be passed to the error dialog
frontend::Alert::warning(_("Uncodable characters"),
bformat(_("The following characters that are used in the inset %1$s are not\n"
"representable in the current encoding and therefore have been omitted:\n%2$s."),
from_utf8(insetType()), uncodable));
}
break;
}
case ParamInfo::HANDLING_ESCAPE:
result = escape(command);
break;
case ParamInfo::HANDLING_NONE:
result = command;
break;
} // switch
return result;
}
docstring InsetCommandParams::getCommand(OutputParams const & runparams) const
{
docstring s = '\\' + from_ascii(cmdName_);
bool noparam = true;
ParamInfo::const_iterator it = info_.begin();
ParamInfo::const_iterator end = info_.end();
for (; it != end; ++it) {
std::string const & name = it->name();
switch (it->type()) {
case ParamInfo::LYX_INTERNAL:
break;
case ParamInfo::LATEX_REQUIRED: {
docstring const data =
prepareCommand(runparams, (*this)[name], it->handling());
s += '{' + data + '}';
noparam = false;
break;
}
case ParamInfo::LATEX_OPTIONAL: {
docstring const data =
prepareCommand(runparams, (*this)[name], it->handling());
if (!data.empty()) {
s += '[' + data + ']';
noparam = false;
} else if (writeEmptyOptional(it)) {
s += "[]";
noparam = false;
}
break;
}
} //end switch
}
if (noparam)
// Make sure that following stuff does not change the
// command name.
s += "{}";
return s;
}
docstring InsetCommandParams::getFirstNonOptParam() const
{
ParamInfo::const_iterator it =
find_if(info_.begin(), info_.end(),
not1(mem_fun_ref(&ParamInfo::ParamData::isOptional)));
if (it == info_.end()) {
LASSERT(false, return docstring());
}
return (*this)[it->name()];
}
docstring const & InsetCommandParams::operator[](string const & name) const
{
static const docstring dummy; //so we don't return a ref to temporary
LASSERT(info_.hasParam(name), return dummy);
ParamMap::const_iterator data = params_.find(name);
if (data == params_.end() || data->second.empty())
return dummy;
return data->second;
}
docstring & InsetCommandParams::operator[](string const & name)
{
LASSERT(info_.hasParam(name), /**/);
return params_[name];
}
void InsetCommandParams::clear()
{
params_.clear();
}
bool operator==(InsetCommandParams const & o1, InsetCommandParams const & o2)
{
return o1.insetCode_ == o2.insetCode_
&& o1.cmdName_ == o2.cmdName_
&& o1.info_ == o2.info_
&& o1.params_ == o2.params_
&& o1.preview_ == o2.preview_;
}
bool operator!=(InsetCommandParams const & o1, InsetCommandParams const & o2)
{
return !(o1 == o2);
}
} // namespace lyx