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 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) {} : type(t), author(a), changetime(ct) {}
/// is the change similar to the given change such that both can be merged? /// is the change similar to the given change such that both can be merged?

View File

@ -24,6 +24,7 @@
#include "Floating.h" #include "Floating.h"
#include "FloatList.h" #include "FloatList.h"
#include "Language.h" #include "Language.h"
#include "LaTeXPackages.h"
#include "Layout.h" #include "Layout.h"
#include "Lexer.h" #include "Lexer.h"
#include "LyXRC.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, LaTeXFeatures::LaTeXFeatures(Buffer const & b, BufferParams const & p,
OutputParams const & r) 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) void LaTeXFeatures::useLayout(docstring const & layoutname)
{ {
// Some code to avoid loops in dependency definition // Some code to avoid loops in dependency definition
@ -441,13 +410,7 @@ bool LaTeXFeatures::isAvailable(string const & name)
//LYXERR0("from=[" << from << "] to=[" << to << "]"); //LYXERR0("from=[" << from << "] to=[" << to << "]");
return theConverters().isReachable(from, to); return theConverters().isReachable(from, to);
} }
return LaTeXPackages::isAvailable(name);
if (packages_.empty())
getAvailable();
string n = name;
if (suffixIs(n, ".sty"))
n.erase(name.length() - 4);
return packages_.find(n) != packages_.end();
} }

View File

@ -86,8 +86,6 @@ public:
void require(std::string const & name); void require(std::string const & name);
/// Add a set of feature names requirements /// Add a set of feature names requirements
void require(std::set<std::string> const & names); void require(std::set<std::string> const & names);
/// Which of the required packages are installed?
static void getAvailable();
/// Is the (required) package available? /// Is the (required) package available?
static bool isAvailable(std::string const & name); static bool isAvailable(std::string const & name);
/// Has the package been required? /// Has the package been required?
@ -151,10 +149,6 @@ private:
typedef std::list<std::string> SnippetList; typedef std::list<std::string> SnippetList;
/// ///
SnippetList preamble_snippets_; SnippetList preamble_snippets_;
/// The available (required) packages
typedef std::set<std::string> Packages;
///
static Packages packages_;
/// ///
typedef std::set<Language const *> LanguageList; typedef std::set<Language const *> LanguageList;
/// used languages (only those that are supported by babel) /// 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 \ Language.cpp \
LaTeX.cpp \ LaTeX.cpp \
LaTeXFeatures.cpp \ LaTeXFeatures.cpp \
LaTeXPackages.cpp \
LayoutFile.cpp \ LayoutFile.cpp \
LayoutModuleList.cpp \ LayoutModuleList.cpp \
Length.cpp \ Length.cpp \
@ -239,6 +240,7 @@ HEADERFILESCORE = \
KeySequence.h \ KeySequence.h \
Language.h \ Language.h \
LaTeXFeatures.h \ LaTeXFeatures.h \
LaTeXPackages.h \
LaTeX.h \ LaTeX.h \
Layout.h \ Layout.h \
LayoutEnums.h \ LayoutEnums.h \

View File

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

View File

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

View File

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

View File

@ -12,9 +12,18 @@
#include "support/lyxtime.h" #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; using namespace std;
namespace lyx { namespace lyx {
namespace support {
time_t current_time() time_t current_time()
{ {
@ -30,4 +39,63 @@ string const formatted_time(time_t t, string const & fmt)
return string(date); 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 } // namespace lyx

View File

@ -18,6 +18,7 @@
namespace lyx { namespace lyx {
namespace support {
time_t current_time(); time_t current_time();
@ -27,6 +28,15 @@ time_t current_time();
*/ */
std::string const formatted_time(time_t t, std::string const & fmt); 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 } // namespace lyx
#endif // LYXTIME_H #endif // LYXTIME_H

View File

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

View File

@ -29,6 +29,7 @@ TEST_FILES = \
test/test-structure.tex test/test-structure.tex
LINKED_FILES = \ LINKED_FILES = \
../Author.cpp \
../Color.cpp \ ../Color.cpp \
../Counters.cpp \ ../Counters.cpp \
../Encoding.cpp \ ../Encoding.cpp \
@ -36,6 +37,7 @@ LINKED_FILES = \
../Floating.cpp \ ../Floating.cpp \
../FontInfo.cpp \ ../FontInfo.cpp \
../insets/InsetLayout.cpp \ ../insets/InsetLayout.cpp \
../LaTeXPackages.cpp \
../Layout.cpp \ ../Layout.cpp \
../LayoutFile.cpp \ ../LayoutFile.cpp \
../LayoutModuleList.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", const char * const known_if_3arg_commands[] = {"@ifundefined", "IfFileExists",
0}; 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. // 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 // Syntax: package_beg_sep<name>package_mid_sep<package loading code>package_end_sep
const char package_beg_sep = '\001'; 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) void Preamble::add_package(string const & name, vector<string> & options)
{ {
// every package inherits the global options // every package inherits the global options
@ -524,6 +551,9 @@ void Preamble::handle_package(Parser &p, string const & name,
add_package(name, options); add_package(name, options);
string scale; string scale;
if (is_known(name, known_xetex_packages))
xetex = true;
// roman fonts // roman fonts
if (is_known(name, known_roman_fonts)) { if (is_known(name, known_roman_fonts)) {
h_font_roman = name; h_font_roman = name;
@ -910,6 +940,7 @@ bool Preamble::writeLyXHeader(ostream & os)
<< "\\html_math_output " << h_html_math_output << "\n" << "\\html_math_output " << h_html_math_output << "\n"
<< "\\html_css_as_file " << h_html_css_as_file << "\n" << "\\html_css_as_file " << h_html_css_as_file << "\n"
<< "\\html_be_strict " << h_html_be_strict << "\n" << "\\html_be_strict " << h_html_be_strict << "\n"
<< authors_
<< "\\end_header\n\n" << "\\end_header\n\n"
<< "\\begin_body\n"; << "\\begin_body\n";
// clear preamble for subdocuments // clear preamble for subdocuments

View File

@ -11,7 +11,7 @@
// {[( // {[(
#include <config.h> #include "Author.h"
#include <iosfwd> #include <iosfwd>
#include <sstream> #include <sstream>
@ -50,6 +50,10 @@ public:
void addModule(std::string const & module); void addModule(std::string const & module);
/// ///
void suppressDate(bool suppress); 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 /// Parses the LaTeX preamble into internal data
@ -152,6 +156,8 @@ private:
std::string const & opts, bool in_lyx_preamble); std::string const & opts, bool in_lyx_preamble);
/// ///
void handle_if(Parser & p, 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 Format LaTeX feature LyX feature
222 change tracking change tracking
224 external insets defined in InsetExternal 224 external insets defined in InsetExternal
lib/external_templates. This is lib/external_templates. This is
quite difficult to recognize. quite difficult to recognize.

View File

@ -16,6 +16,8 @@
\newcommand{\noun}[1]{\textsc{#1}} \newcommand{\noun}[1]{\textsc{#1}}
%% Because html converters don't know tabularnewline %% Because html converters don't know tabularnewline
\providecommand{\tabularnewline}{\\} \providecommand{\tabularnewline}{\\}
\newcommand{\lyxadded}[3]{#3}
\newcommand{\lyxdeleted}[3]{}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Textclass specific LaTeX commands. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Textclass specific LaTeX commands.
\newenvironment{lyxlist}[1] \newenvironment{lyxlist}[1]
@ -87,6 +89,12 @@ This causes the \strong{logikalmkup} module to be loaded.
An environment An environment
\end{quote} \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} \section*{A starred section for floats}
\begin{figure} \begin{figure}

View File

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

View File

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

View File

@ -19,6 +19,7 @@
#include "Context.h" #include "Context.h"
#include "Encoding.h" #include "Encoding.h"
#include "FloatList.h" #include "FloatList.h"
#include "LaTeXPackages.h"
#include "Layout.h" #include "Layout.h"
#include "Length.h" #include "Length.h"
#include "Preamble.h" #include "Preamble.h"
@ -28,6 +29,7 @@
#include "support/FileName.h" #include "support/FileName.h"
#include "support/filetools.h" #include "support/filetools.h"
#include "support/lstrings.h" #include "support/lstrings.h"
#include "support/lyxtime.h"
#include <algorithm> #include <algorithm>
#include <iostream> #include <iostream>
@ -2703,6 +2705,50 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
preamble.registerAutomaticallyLoadedPackage("ulem"); 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" || else if (t.cs() == "phantom" || t.cs() == "hphantom" ||
t.cs() == "vphantom") { t.cs() == "vphantom") {
context.check_layout(os); context.check_layout(os);