Fix bug #4213: Change tracking support for tex2lyx.

Of course this will only work if output_changes was true when creating the
.tex file.


git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@40139 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
Georg Baum 2011-11-06 17:03:59 +00:00
parent 713c14b71d
commit 0dee3eff18
20 changed files with 301 additions and 53 deletions

View File

@ -41,7 +41,7 @@ public:
DELETED // deleted text
};
explicit Change(Type t = UNCHANGED, int a = 0, time_t ct = current_time())
explicit Change(Type t = UNCHANGED, int a = 0, time_t ct = support::current_time())
: type(t), author(a), changetime(ct) {}
/// is the change similar to the given change such that both can be merged?

View File

@ -24,6 +24,7 @@
#include "Floating.h"
#include "FloatList.h"
#include "Language.h"
#include "LaTeXPackages.h"
#include "Layout.h"
#include "Lexer.h"
#include "LyXRC.h"
@ -281,8 +282,6 @@ static docstring const lyxref_def = from_ascii(
//
/////////////////////////////////////////////////////////////////////
LaTeXFeatures::Packages LaTeXFeatures::packages_;
LaTeXFeatures::LaTeXFeatures(Buffer const & b, BufferParams const & p,
OutputParams const & r)
@ -334,36 +333,6 @@ void LaTeXFeatures::require(set<string> const & names)
}
void LaTeXFeatures::getAvailable()
{
Lexer lex;
support::FileName const real_file = libFileSearch("", "packages.lst");
if (real_file.empty())
return;
lex.setFile(real_file);
if (!lex.isOK())
return;
// Make sure that we are clean
packages_.clear();
bool finished = false;
// Parse config-file
while (lex.isOK() && !finished) {
switch (lex.lex()) {
case Lexer::LEX_FEOF:
finished = true;
break;
default:
packages_.insert(lex.getString());
}
}
}
void LaTeXFeatures::useLayout(docstring const & layoutname)
{
// Some code to avoid loops in dependency definition
@ -441,13 +410,7 @@ bool LaTeXFeatures::isAvailable(string const & name)
//LYXERR0("from=[" << from << "] to=[" << to << "]");
return theConverters().isReachable(from, to);
}
if (packages_.empty())
getAvailable();
string n = name;
if (suffixIs(n, ".sty"))
n.erase(name.length() - 4);
return packages_.find(n) != packages_.end();
return LaTeXPackages::isAvailable(name);
}

View File

@ -86,8 +86,6 @@ public:
void require(std::string const & name);
/// Add a set of feature names requirements
void require(std::set<std::string> const & names);
/// Which of the required packages are installed?
static void getAvailable();
/// Is the (required) package available?
static bool isAvailable(std::string const & name);
/// Has the package been required?
@ -151,10 +149,6 @@ private:
typedef std::list<std::string> SnippetList;
///
SnippetList preamble_snippets_;
/// The available (required) packages
typedef std::set<std::string> Packages;
///
static Packages packages_;
///
typedef std::set<Language const *> LanguageList;
/// used languages (only those that are supported by babel)

75
src/LaTeXPackages.cpp Normal file
View File

@ -0,0 +1,75 @@
/**
* \file LaTeXPackages.cpp
* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
* \author José Matos
* \author Lars Gullik Bjønnes
* \author Jean-Marc Lasgouttes
* \author Jürgen Vigna
* \author André Pönitz
*
* Full author contact details are available in file CREDITS.
*/
#include <config.h>
#include "LaTeXPackages.h"
#include "Lexer.h"
#include "support/FileName.h"
#include "support/filetools.h"
#include "support/lstrings.h"
using namespace std;
using namespace lyx::support;
namespace lyx {
LaTeXPackages::Packages LaTeXPackages::packages_;
void LaTeXPackages::getAvailable()
{
Lexer lex;
support::FileName const real_file = libFileSearch("", "packages.lst");
if (real_file.empty())
return;
lex.setFile(real_file);
if (!lex.isOK())
return;
// Make sure that we are clean
packages_.clear();
bool finished = false;
// Parse config-file
while (lex.isOK() && !finished) {
switch (lex.lex()) {
case Lexer::LEX_FEOF:
finished = true;
break;
default:
packages_.insert(lex.getString());
}
}
}
bool LaTeXPackages::isAvailable(string const & name)
{
if (packages_.empty())
getAvailable();
string n = name;
if (suffixIs(n, ".sty"))
n.erase(name.length() - 4);
return packages_.find(n) != packages_.end();
}
} // namespace lyx

41
src/LaTeXPackages.h Normal file
View File

@ -0,0 +1,41 @@
// -*- C++ -*-
/**
* \file LaTeXPackages.h
* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
* \author Lars Gullik Bjønnes
* \author Jean-Marc Lasgouttes
*
* Full author contact details are available in file CREDITS.
*/
#ifndef LATEXPACKAGES_H
#define LATEXPACKAGES_H
#include <string>
#include <set>
namespace lyx {
/** The list of avilable LaTeX packages
*/
class LaTeXPackages {
public:
/// Which of the required packages are installed?
static void getAvailable();
/// Is the (required) package available?
static bool isAvailable(std::string const & name);
private:
/// The available (required) packages
typedef std::set<std::string> Packages;
///
static Packages packages_;
};
} // namespace lyx
#endif

View File

@ -141,6 +141,7 @@ SOURCEFILESCORE = \
Language.cpp \
LaTeX.cpp \
LaTeXFeatures.cpp \
LaTeXPackages.cpp \
LayoutFile.cpp \
LayoutModuleList.cpp \
Length.cpp \
@ -239,6 +240,7 @@ HEADERFILESCORE = \
KeySequence.h \
Language.h \
LaTeXFeatures.h \
LaTeXPackages.h \
LaTeX.h \
Layout.h \
LayoutEnums.h \

View File

@ -14,7 +14,6 @@
#define SPELL_BASE_H
#include "support/strfwd.h"
#include "support/lyxtime.h"
namespace lyx {

View File

@ -41,7 +41,7 @@
#include "Intl.h"
#include "KeyMap.h"
#include "Language.h"
#include "LaTeXFeatures.h"
#include "LaTeXPackages.h"
#include "Lexer.h"
#include "LyX.h"
#include "LyXAction.h"
@ -1211,7 +1211,7 @@ void GuiApplication::reconfigure(string const & option)
current_view_->message(_("Reloading configuration..."));
lyxrc.read(libFileSearch(QString(), "lyxrc.defaults"), false);
// Re-read packages.lst
LaTeXFeatures::getAvailable();
LaTeXPackages::getAvailable();
if (ret)
Alert::information(_("System reconfiguration failed"),

View File

@ -34,6 +34,7 @@ namespace lyx {
namespace frontend {
using support::bformat;
using support::formatted_time;
GuiChanges::GuiChanges(GuiView & lv)
: GuiDialog(lv, "changes", qt_("Merge Changes"))

View File

@ -12,9 +12,18 @@
#include "support/lyxtime.h"
#include "support/debug.h"
#include "support/environment.h"
#include "support/lstrings.h"
#include "support/qstring_helpers.h"
#include <QDateTime>
#include <QLocale>
using namespace std;
namespace lyx {
namespace support {
time_t current_time()
{
@ -30,4 +39,63 @@ string const formatted_time(time_t t, string const & fmt)
return string(date);
}
time_t from_ctime(string t)
{
// Example for the format: "Sun Nov 6 10:39:39 2011\n"
// Generously remove trailing '\n' (and other whitespace if needed)
t = trim(t, " \t\r\n");
#if QT_VERSION >= 0x040400
// toDateTime() is too stupid to recognize variable amounts of
// whitespace (needed because ctime() outputs double spaces before
// single digit day numbers and hours)
t = subst(t, " ", " ");
QString const format("ddd MMM d H:mm:ss yyyy");
QLocale loc("C");
QDateTime loc_dt = loc.toDateTime(toqstr(t), format);
if (!loc_dt.isValid()) {
LYXERR(Debug::LOCALE, "Could not parse `" << t
<< "´ (invalid format)");
return static_cast<time_t>(-1);
}
return loc_dt.toTime_t();
#elif defined(_WIN32)
#error "The minimum required Qt version on windows is Qt 4.4."
#else
// strptime() is not available on windows (defined by POSIX)
// strptime() uses the current locale, so we need to switch to "C"
LYXERR(Debug::LOCALE, "Setting LC_ALL and LC_TIME to C");
string oldLC_ALL = getEnv("LC_ALL");
string oldLC_TIME = getEnv("LC_TIME");
if (!setEnv("LC_ALL", "C"))
LYXERR(Debug::LOCALE, "\t... LC_ALL failed!");
if (!setEnv("LC_TIME", "C"))
LYXERR(Debug::LOCALE, "\t... LC_TIME failed!");
struct tm loc_tm;
char const * const format = "%a%n%b%n%d%n%T%n%Y";
char * remainder = strptime(t.c_str(), format, &loc_tm);
LYXERR(Debug::LOCALE, "Resetting LC_ALL and LC_TIME");
if(!setEnv("LC_TIME", oldLC_TIME))
LYXERR(Debug::LOCALE, "\t... LC_TIME failed!");
if (!setEnv("LC_ALL", oldLC_ALL))
LYXERR(Debug::LOCALE, "\t... LC_ALL failed!");
if (!remainder) {
LYXERR(Debug::LOCALE, "Could not parse `" << t
<< "´ (invalid format)");
return static_cast<time_t>(-1);
}
if (*remainder != '\0') {
LYXERR(Debug::LOCALE, "Could not parse `" << t
<< "´ (excess characters)");
return static_cast<time_t>(-1);
}
return mktime(&loc_tm);
#endif
}
} // namespace support
} // namespace lyx

View File

@ -18,6 +18,7 @@
namespace lyx {
namespace support {
time_t current_time();
@ -27,6 +28,15 @@ time_t current_time();
*/
std::string const formatted_time(time_t t, std::string const & fmt);
/**
* Inverse of ctime().
* Since ctime() outputs the local time, the caller needs to ensure that the
* time zone and daylight saving time are the same as when \p t was created
* by ctime().
*/
time_t from_ctime(std::string t);
} // namespace support
} // namespace lyx
#endif // LYXTIME_H

View File

@ -12,8 +12,8 @@ project(${_tex2lyx})
set(LINKED_sources ${TOP_SRC_DIR}/src/lengthcommon.cpp)
set(LINKED_headers)
foreach(_src insets/InsetLayout Color Counters
Encoding FloatList Floating FontInfo
foreach(_src insets/InsetLayout Author Color Counters
Encoding FloatList Floating FontInfo LaTeXPackages
Layout LayoutFile LayoutModuleList Lexer ModuleList TextClass
Spacing version)
list(APPEND LINKED_sources ${TOP_SRC_DIR}/src/${_src}.cpp)

View File

@ -29,6 +29,7 @@ TEST_FILES = \
test/test-structure.tex
LINKED_FILES = \
../Author.cpp \
../Color.cpp \
../Counters.cpp \
../Encoding.cpp \
@ -36,6 +37,7 @@ LINKED_FILES = \
../Floating.cpp \
../FontInfo.cpp \
../insets/InsetLayout.cpp \
../LaTeXPackages.cpp \
../Layout.cpp \
../LayoutFile.cpp \
../LayoutModuleList.cpp \

View File

@ -165,6 +165,11 @@ const char * const known_basic_color_codes[] = {"#0000ff", "#000000", "#00ffff",
const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
0};
/// packages that work only in xetex
const char * const known_xetex_packages[] = {"arabxetex", "fixlatvian",
"fontbook", "fontwrap", "mathspec", "philokalia", "polyglossia", "unisugar",
"xeCJK", "xecolor", "xecyr", "xeindex", "xepersian", "xunicode", 0};
// codes used to remove packages that are loaded automatically by LyX.
// Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
const char package_beg_sep = '\001';
@ -313,6 +318,28 @@ void Preamble::suppressDate(bool suppress)
}
void Preamble::registerAuthor(std::string const & name)
{
Author author(from_utf8(name), empty_docstring());
author.setUsed(true);
authors_.record(author);
h_tracking_changes = "true";
h_output_changes = "true";
}
Author const & Preamble::getAuthor(std::string const & name) const
{
Author author(from_utf8(name), empty_docstring());
for (AuthorList::Authors::const_iterator it = authors_.begin();
it != authors_.end(); it++)
if (*it == author)
return *it;
static Author const dummy;
return dummy;
}
void Preamble::add_package(string const & name, vector<string> & options)
{
// every package inherits the global options
@ -524,6 +551,9 @@ void Preamble::handle_package(Parser &p, string const & name,
add_package(name, options);
string scale;
if (is_known(name, known_xetex_packages))
xetex = true;
// roman fonts
if (is_known(name, known_roman_fonts)) {
h_font_roman = name;
@ -910,6 +940,7 @@ bool Preamble::writeLyXHeader(ostream & os)
<< "\\html_math_output " << h_html_math_output << "\n"
<< "\\html_css_as_file " << h_html_css_as_file << "\n"
<< "\\html_be_strict " << h_html_be_strict << "\n"
<< authors_
<< "\\end_header\n\n"
<< "\\begin_body\n";
// clear preamble for subdocuments

View File

@ -11,7 +11,7 @@
// {[(
#include <config.h>
#include "Author.h"
#include <iosfwd>
#include <sstream>
@ -50,6 +50,10 @@ public:
void addModule(std::string const & module);
///
void suppressDate(bool suppress);
/// Register an author named \p name in the author list
void registerAuthor(std::string const & name);
/// Get author named \p name (must be registered first)
Author const & getAuthor(std::string const & name) const;
/// Parses the LaTeX preamble into internal data
@ -152,6 +156,8 @@ private:
std::string const & opts, bool in_lyx_preamble);
///
void handle_if(Parser & p, bool in_lyx_preamble);
AuthorList authors_;
};

View File

@ -10,7 +10,6 @@ LyX feature: LyX inset or document setting
Format LaTeX feature LyX feature
222 change tracking change tracking
224 external insets defined in InsetExternal
lib/external_templates. This is
quite difficult to recognize.

View File

@ -16,6 +16,8 @@
\newcommand{\noun}[1]{\textsc{#1}}
%% Because html converters don't know tabularnewline
\providecommand{\tabularnewline}{\\}
\newcommand{\lyxadded}[3]{#3}
\newcommand{\lyxdeleted}[3]{}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Textclass specific LaTeX commands.
\newenvironment{lyxlist}[1]
@ -87,6 +89,12 @@ This causes the \strong{logikalmkup} module to be loaded.
An environment
\end{quote}
We also support change tracking:
\lyxadded{Hans Wurst}{Sun Nov 6 10:39:39 2011}{Added text}
some parts remain
\lyxdeleted{Hans Wurst}{Sun Nov 6 10:39:55 2011}{This was the original text}
some parts remain
\section*{A starred section for floats}
\begin{figure}

View File

@ -332,6 +332,7 @@ bool checkModule(string const & name, bool command)
bool noweb_mode = false;
bool pdflatex = false;
bool xetex = false;
bool roundtrip = false;

View File

@ -158,6 +158,8 @@ extern FullEnvironmentMap possible_textclass_environments;
extern bool noweb_mode;
/// Did we recognize any pdflatex-only construct?
extern bool pdflatex;
/// Did we recognize any xetex-only construct?
extern bool xetex;
/// LyX format that is created by tex2lyx
extern int const LYX_FORMAT;

View File

@ -19,6 +19,7 @@
#include "Context.h"
#include "Encoding.h"
#include "FloatList.h"
#include "LaTeXPackages.h"
#include "Layout.h"
#include "Length.h"
#include "Preamble.h"
@ -28,6 +29,7 @@
#include "support/FileName.h"
#include "support/filetools.h"
#include "support/lstrings.h"
#include "support/lyxtime.h"
#include <algorithm>
#include <iostream>
@ -2703,6 +2705,50 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
preamble.registerAutomaticallyLoadedPackage("ulem");
}
else if (t.cs() == "lyxadded" || t.cs() == "lyxdeleted") {
context.check_layout(os);
string name = p.getArg('{', '}');
string localtime = p.getArg('{', '}');
preamble.registerAuthor(name);
Author const & author = preamble.getAuthor(name);
// from_ctime() will fail if LyX decides to output the
// time in the text language. It might also use a wrong
// time zone (if the original LyX document was exported
// with a different time zone).
time_t ptime = from_ctime(localtime);
if (ptime == static_cast<time_t>(-1)) {
cerr << "Warning: Could not parse time `" << localtime
<< "´ for change tracking, using current time instead.\n";
ptime = current_time();
}
if (t.cs() == "lyxadded")
os << "\n\\change_inserted ";
else
os << "\n\\change_deleted ";
os << author.bufferId() << ' ' << ptime << '\n';
parse_text_snippet(p, os, FLAG_ITEM, outer, context);
bool dvipost = LaTeXPackages::isAvailable("dvipost");
bool xcolorulem = LaTeXPackages::isAvailable("ulem") &&
LaTeXPackages::isAvailable("xcolor");
// No need to test for luatex, since luatex comes in
// two flavours (dvi and pdf), like latex, and those
// are detected by pdflatex.
if (pdflatex || xetex) {
if (xcolorulem) {
preamble.registerAutomaticallyLoadedPackage("ulem");
preamble.registerAutomaticallyLoadedPackage("xcolor");
preamble.registerAutomaticallyLoadedPackage("pdfcolmk");
}
} else {
if (dvipost) {
preamble.registerAutomaticallyLoadedPackage("dvipost");
} else if (xcolorulem) {
preamble.registerAutomaticallyLoadedPackage("ulem");
preamble.registerAutomaticallyLoadedPackage("xcolor");
}
}
}
else if (t.cs() == "phantom" || t.cs() == "hphantom" ||
t.cs() == "vphantom") {
context.check_layout(os);