Implement a native IPA inset (bug #2591) with instant preview.

Currently only basic functionality (our math-tipa functionality plus multipar input). Further enhancements (such as proper unicode input and a specific toolbar) are planned. DocBook and XHTML output also need to be audited.

The lyx2lyx reversion routine surely can be improved (help appreciated), and I don't know  tex2lyx enough to handle this file format change.

git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@40867 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
Jürgen Spitzmüller 2012-03-06 07:54:22 +00:00
parent 24733efcee
commit f626cfb7a3
11 changed files with 366 additions and 3 deletions

View File

@ -535,6 +535,44 @@ def revert_verbatim(document):
document.body[i:i+1] = subst_begin document.body[i:i+1] = subst_begin
def revert_tipa(document):
" Revert native TIPA insets to mathed or ERT. "
i = 0
while 1:
i = find_token(document.body, "\\begin_inset IPA", i)
if i == -1:
return
j = find_end_of_inset(document.body, i)
if j == -1:
document.warning("Malformed lyx document: Can't find end of IPA inset")
i += 1
continue
Multipar = False
n = find_token(document.body, "\\begin_layout", i, j)
if n == -1:
document.warning("Malformed lyx document: IPA inset has no embedded layout")
i += 1
continue
m = find_end_of_layout(document.body, n)
if m == -1:
document.warning("Malformed lyx document: Can't find end of embedded layout")
i += 1
continue
content = document.body[n+1:m]
p = find_token(document.body, "\\begin_layout", m, j)
if p != -1 or len(content) > 1:
Multipar = True
content = document.body[i+1:j]
if Multipar:
# IPA insets with multiple pars need to be wrapped by \begin{IPA}...\end{IPA}
document.body[i:j+1] = ['\\end_layout', '', '\\begin_layout Standard'] + put_cmd_in_ert("\\begin{IPA}") + ['\\end_layout'] + content + ['\\begin_layout Standard'] + put_cmd_in_ert("\\end{IPA}")
add_to_preamble(document, ["\\usepackage{tipa,tipx}"])
else:
# single-par IPA insets can be reverted to mathed
document.body[i:j+1] = ["\\begin_inset Formula $\\text{\\textipa{" + content[0] + "}}$", "\\end_inset"]
i = j
## ##
# Conversion hub # Conversion hub
# #
@ -553,10 +591,12 @@ convert = [
[423, [convert_use_mathtools]], [423, [convert_use_mathtools]],
[424, [convert_cite_engine_type]], [424, [convert_cite_engine_type]],
[425, []], [425, []],
[426, []] [426, []],
[427, []]
] ]
revert = [ revert = [
[426, [revert_tipa]],
[425, [revert_verbatim]], [425, [revert_verbatim]],
[424, [revert_cancel]], [424, [revert_cancel]],
[423, [revert_cite_engine_type]], [423, [revert_cite_engine_type]],

View File

@ -388,7 +388,7 @@ Menuset
Item "Protected Hyphen|y" "specialchar-insert nobreakdash" Item "Protected Hyphen|y" "specialchar-insert nobreakdash"
Item "Breakable Slash|a" "specialchar-insert slash" Item "Breakable Slash|a" "specialchar-insert slash"
Item "Menu Separator|M" "specialchar-insert menu-separator" Item "Menu Separator|M" "specialchar-insert menu-separator"
Item "Phonetic Symbols|P" "command-sequence math-insert \text\textipa ; char-forward ;" Item "Phonetic Symbols|P" "ipa-insert"
End End
Menu "insert_formatting" Menu "insert_formatting"

View File

@ -451,6 +451,7 @@ enum FuncCode
LFUN_BUFFER_EXPORT_AS, // tommaso 20111006 LFUN_BUFFER_EXPORT_AS, // tommaso 20111006
// 350 // 350
LFUN_CLIPBOARD_PASTE_SIMPLE, // tommaso, 20111028 LFUN_CLIPBOARD_PASTE_SIMPLE, // tommaso, 20111028
LFUN_IPA_INSERT, // spitz, 20120305
LFUN_LASTACTION // end of the table LFUN_LASTACTION // end of the table
}; };

View File

@ -542,6 +542,14 @@ void LyXAction::init()
* \endvar * \endvar
*/ */
{ LFUN_INDEX_PRINT, "index-print", Noop, Edit }, { LFUN_INDEX_PRINT, "index-print", Noop, Edit },
/*!
* \var lyx::FuncCode lyx::LFUN_IPA_INSERT
* \li Action: Inserts an IPA inset.
* \li Syntax: ipa-insert
* \li Origin: spitz, 05 Mar 2012
* \endvar
*/
{ LFUN_IPA_INSERT, "ipa-insert", Noop, Edit },
/*! /*!
* \var lyx::FuncCode lyx::LFUN_NOMENCL_INSERT * \var lyx::FuncCode lyx::LFUN_NOMENCL_INSERT

View File

@ -558,6 +558,7 @@ SOURCEFILESINSETS = \
insets/InsetInclude.cpp \ insets/InsetInclude.cpp \
insets/InsetIndex.cpp \ insets/InsetIndex.cpp \
insets/InsetInfo.cpp \ insets/InsetInfo.cpp \
insets/InsetIPA.cpp \
insets/InsetLabel.cpp \ insets/InsetLabel.cpp \
insets/InsetLayout.cpp \ insets/InsetLayout.cpp \
insets/InsetLine.cpp \ insets/InsetLine.cpp \
@ -614,6 +615,7 @@ HEADERFILESINSETS = \
insets/InsetInclude.h \ insets/InsetInclude.h \
insets/InsetIndex.h \ insets/InsetIndex.h \
insets/InsetInfo.h \ insets/InsetInfo.h \
insets/InsetIPA.h \
insets/InsetPreview.h \ insets/InsetPreview.h \
insets/InsetLabel.h \ insets/InsetLabel.h \
insets/InsetLayout.h \ insets/InsetLayout.h \

View File

@ -1649,6 +1649,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
case LFUN_INDEX_INSERT: case LFUN_INDEX_INSERT:
case LFUN_PREVIEW_INSERT: case LFUN_PREVIEW_INSERT:
case LFUN_SCRIPT_INSERT: case LFUN_SCRIPT_INSERT:
case LFUN_IPA_INSERT:
// Open the inset, and move the current selection // Open the inset, and move the current selection
// inside it. // inside it.
doInsertInset(cur, this, cmd, true, true); doInsertInset(cur, this, cmd, true, true);
@ -2445,6 +2446,9 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd,
&& cur.buffer()->params().branchlist().empty()) && cur.buffer()->params().branchlist().empty())
enable = false; enable = false;
break; break;
case LFUN_IPA_INSERT:
code = IPA_CODE;
break;
case LFUN_PHANTOM_INSERT: case LFUN_PHANTOM_INSERT:
code = PHANTOM_CODE; code = PHANTOM_CODE;
break; break;

View File

@ -38,6 +38,7 @@
#include "insets/InsetInclude.h" #include "insets/InsetInclude.h"
#include "insets/InsetIndex.h" #include "insets/InsetIndex.h"
#include "insets/InsetInfo.h" #include "insets/InsetInfo.h"
#include "insets/InsetIPA.h"
#include "insets/InsetLabel.h" #include "insets/InsetLabel.h"
#include "insets/InsetLine.h" #include "insets/InsetLine.h"
#include "insets/InsetMarginal.h" #include "insets/InsetMarginal.h"
@ -184,6 +185,9 @@ Inset * createInsetHelper(Buffer * buf, FuncRequest const & cmd)
return new InsetIndex(buf, InsetIndexParams(arg)); return new InsetIndex(buf, InsetIndexParams(arg));
} }
case LFUN_IPA_INSERT:
return new InsetIPA(buf);
case LFUN_NOMENCL_INSERT: { case LFUN_NOMENCL_INSERT: {
InsetCommandParams icp(NOMENCL_CODE); InsetCommandParams icp(NOMENCL_CODE);
icp["symbol"] = cmd.argument(); icp["symbol"] = cmd.argument();
@ -615,6 +619,8 @@ Inset * readInset(Lexer & lex, Buffer * buf)
inset.reset(new InsetFloatList(buf)); inset.reset(new InsetFloatList(buf));
} else if (tmptok == "Info") { } else if (tmptok == "Info") {
inset.reset(new InsetInfo(buf)); inset.reset(new InsetInfo(buf));
} else if (tmptok == "IPA") {
inset.reset(new InsetIPA(buf));
} else if (tmptok == "Preview") { } else if (tmptok == "Preview") {
inset.reset(new InsetPreview(buf)); inset.reset(new InsetPreview(buf));
} else { } else {

View File

@ -231,6 +231,8 @@ enum InsetCode {
/// ///
SCRIPT_CODE, // 105 SCRIPT_CODE, // 105
/// ///
IPA_CODE,
///
INSET_CODE_SIZE INSET_CODE_SIZE
}; };

199
src/insets/InsetIPA.cpp Normal file
View File

@ -0,0 +1,199 @@
/**
* \file InsetIPA.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 "InsetIPA.h"
#include "Buffer.h"
#include "BufferParams.h"
#include "BufferView.h"
#include "Cursor.h"
#include "LaTeXFeatures.h"
#include "Lexer.h"
#include "LyXRC.h"
#include "MetricsInfo.h"
#include "OutputParams.h"
#include "RenderPreview.h"
#include "frontends/Painter.h"
#include "graphics/PreviewImage.h"
#include <sstream>
using namespace std;
namespace lyx {
InsetIPA::InsetIPA(Buffer * buf)
: InsetText(buf),
preview_(new RenderPreview(this)), use_preview_(true)
{
setAutoBreakRows(true);
setDrawFrame(true);
setFrameColor(Color_insetframe);
}
InsetIPA::~InsetIPA()
{}
InsetIPA::InsetIPA(InsetIPA const & other)
: InsetText(other)
{
preview_.reset(new RenderPreview(*other.preview_, this));
}
void InsetIPA::write(ostream & os) const
{
os << "IPA" << "\n";
text().write(os);
}
void InsetIPA::addPreview(DocIterator const & inset_pos,
graphics::PreviewLoader &) const
{
preparePreview(inset_pos);
}
void InsetIPA::preparePreview(DocIterator const & pos) const
{
TexRow texrow;
odocstringstream str;
otexstream os(str, texrow);
OutputParams runparams(&pos.buffer()->params().encoding());
latex(os, runparams);
docstring const snippet = str.str();
preview_->addPreview(snippet, *pos.buffer());
}
bool InsetIPA::previewState(BufferView * bv) const
{
if (!editing(bv) && (RenderPreview::status() == LyXRC::PREVIEW_ON ||
RenderPreview::status() == LyXRC::PREVIEW_NO_MATH)) {
graphics::PreviewImage const * pimage =
preview_->getPreviewImage(bv->buffer());
return pimage && pimage->image();
}
return false;
}
void InsetIPA::reloadPreview(DocIterator const & pos) const
{
preparePreview(pos);
preview_->startLoading(*pos.buffer());
}
void InsetIPA::draw(PainterInfo & pi, int x, int y) const
{
use_preview_ = previewState(pi.base.bv);
if (use_preview_) {
preview_->draw(pi, x + TEXT_TO_INSET_OFFSET, y);
setPosCache(pi, x, y);
return;
}
InsetText::draw(pi, x, y);
}
void InsetIPA::edit(Cursor & cur, bool front, EntryDirection entry_from)
{
cur.push(*this);
InsetText::edit(cur, front, entry_from);
}
Inset * InsetIPA::editXY(Cursor & cur, int x, int y)
{
if (use_preview_) {
edit(cur, true, ENTRY_DIRECTION_IGNORE);
return this;
}
cur.push(*this);
return InsetText::editXY(cur, x, y);
}
void InsetIPA::metrics(MetricsInfo & mi, Dimension & dim) const
{
if (previewState(mi.base.bv)) {
preview_->metrics(mi, dim);
mi.base.textwidth += 2 * TEXT_TO_INSET_OFFSET;
dim.wid = max(dim.wid, 4);
dim.asc = max(dim.asc, 4);
dim.asc += TEXT_TO_INSET_OFFSET;
dim.des += TEXT_TO_INSET_OFFSET;
dim.wid += TEXT_TO_INSET_OFFSET;
dim_ = dim;
dim.wid += TEXT_TO_INSET_OFFSET;
// insert a one pixel gap
dim.wid += 1;
// Cache the inset dimension.
setDimCache(mi, dim);
Dimension dim_dummy;
MetricsInfo mi_dummy = mi;
InsetText::metrics(mi_dummy, dim_dummy);
return;
}
InsetText::metrics(mi, dim);
}
bool InsetIPA::notifyCursorLeaves(Cursor const & old, Cursor & cur)
{
reloadPreview(old);
cur.screenUpdateFlags(Update::Force);
return InsetText::notifyCursorLeaves(old, cur);
}
void InsetIPA::validate(LaTeXFeatures & features) const
{
features.require("tipa");
features.require("tipx");
}
void InsetIPA::latex(otexstream & os, OutputParams const & runparams) const
{
bool const multipar = (text().paragraphs().size() > 1);
if (multipar)
os << "\\begin{IPA}\n";
else
os << "\\textipa{";
InsetText::latex(os, runparams);
if (multipar)
os << "\n\\end{IPA}";
else
os << "}";
}
bool InsetIPA::insetAllowed(InsetCode code) const
{
if (code == ERT_CODE)
return true;
else
return false;
}
} // namespace lyx

101
src/insets/InsetIPA.h Normal file
View File

@ -0,0 +1,101 @@
// -*- C++ -*-
/**
* \file InsetIPA.h
* 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.
*/
#ifndef INSETIPA_H
#define INSETIPA_H
#include "InsetText.h"
#include "Dimension.h"
#include <boost/scoped_ptr.hpp>
namespace lyx {
class RenderPreview;
namespace graphics {
class PreviewLoader;
}
/// An IPA inset with instant preview
class InsetIPA : public InsetText {
public:
///
InsetIPA(Buffer *);
///
~InsetIPA();
///
InsetIPA(InsetIPA const & other);
/// \name Methods inherited from Inset class
//@{
Inset * clone() const { return new InsetIPA(*this); }
bool neverIndent() const { return true; }
InsetCode lyxCode() const { return IPA_CODE; }
docstring layoutName() const { return from_ascii("IPA"); }
bool descendable(BufferView const & /*bv*/) const { return true; }
void metrics(MetricsInfo & mi, Dimension & dim) const;
Inset * editXY(Cursor & cur, int x, int y);
void draw(PainterInfo & pi, int x, int y) const;
void addPreview(DocIterator const & inset_pos,
graphics::PreviewLoader & ploader) const;
bool notifyCursorLeaves(Cursor const & old, Cursor & cur);
void write(std::ostream & os) const;
void edit(Cursor & cur, bool front, EntryDirection entry_from);
///
void latex(otexstream &, OutputParams const &) const;
///
void validate(LaTeXFeatures & features) const;
///
bool allowSpellCheck() const { return false; }
///
bool insetAllowed(InsetCode code) const;
//@}
protected:
/// Retrieves the preview state. Returns true if preview
/// is enabled and the preview image is availabled.
bool previewState(BufferView * bv) const;
/// Recreates the preview if preview is enabled.
void reloadPreview(DocIterator const & pos) const;
/// Prepare the preview if preview is enabled.
void preparePreview(DocIterator const & pos) const;
///
boost::scoped_ptr<RenderPreview> preview_;
///
mutable bool use_preview_;
private:
///
mutable Dimension dim_;
};
} // namespace lyx
#endif

View File

@ -30,7 +30,7 @@ extern char const * const lyx_version_info;
// Do not remove the comment below, so we get merge conflict in // Do not remove the comment below, so we get merge conflict in
// independent branches. Instead add your own. // independent branches. Instead add your own.
#define LYX_FORMAT_LYX 426 // uwestoehr: support for verbatim #define LYX_FORMAT_LYX 427 // spitz: tipa inset
#define LYX_FORMAT_TEX2LYX 426 // uwestoehr: support for verbatim #define LYX_FORMAT_TEX2LYX 426 // uwestoehr: support for verbatim
#if LYX_FORMAT_FOR_TEX2LYX != LYX_FORMAT_FOR_LYX #if LYX_FORMAT_FOR_TEX2LYX != LYX_FORMAT_FOR_LYX