2003-02-08 19:18:01 +00:00
|
|
|
/**
|
2007-04-26 04:41:58 +00:00
|
|
|
* \file Changes.cpp
|
2003-08-23 00:17:00 +00:00
|
|
|
* This file is part of LyX, the document processor.
|
|
|
|
* Licence details can be found in the file COPYING.
|
2003-02-08 19:18:01 +00:00
|
|
|
*
|
2003-08-23 00:17:00 +00:00
|
|
|
* \author John Levon
|
2006-10-21 14:34:05 +00:00
|
|
|
* \author Michael Gerz
|
2003-08-23 00:17:00 +00:00
|
|
|
*
|
|
|
|
* Full author contact details are available in file CREDITS.
|
2003-02-08 19:18:01 +00:00
|
|
|
*
|
2003-08-23 00:17:00 +00:00
|
|
|
* Record changes in a paragraph.
|
2003-02-08 19:18:01 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
2003-03-04 09:27:27 +00:00
|
|
|
|
2007-04-26 04:41:58 +00:00
|
|
|
#include "Changes.h"
|
2007-05-08 17:46:03 +00:00
|
|
|
#include "Author.h"
|
2008-09-30 11:06:34 +00:00
|
|
|
#include "Buffer.h"
|
2007-05-08 17:46:03 +00:00
|
|
|
#include "BufferParams.h"
|
2009-10-12 16:22:05 +00:00
|
|
|
#include "Encoding.h"
|
2019-12-27 09:35:52 +00:00
|
|
|
#include "LyXRC.h"
|
2016-05-22 21:48:28 +00:00
|
|
|
#include "MetricsInfo.h"
|
2009-10-12 16:22:05 +00:00
|
|
|
#include "OutputParams.h"
|
2008-09-30 11:06:34 +00:00
|
|
|
#include "Paragraph.h"
|
2016-06-19 02:39:38 +00:00
|
|
|
#include "texstream.h"
|
2008-09-30 11:06:34 +00:00
|
|
|
#include "TocBackend.h"
|
2003-03-04 09:27:27 +00:00
|
|
|
|
2007-11-29 07:04:28 +00:00
|
|
|
#include "support/debug.h"
|
2008-09-30 11:06:34 +00:00
|
|
|
#include "support/gettext.h"
|
2008-04-30 08:26:40 +00:00
|
|
|
#include "support/lassert.h"
|
2009-10-12 16:22:05 +00:00
|
|
|
#include "support/lstrings.h"
|
2014-07-05 12:49:51 +00:00
|
|
|
#include "support/mutex.h"
|
2009-10-12 16:22:05 +00:00
|
|
|
|
|
|
|
#include "frontends/alert.h"
|
2016-05-22 21:48:28 +00:00
|
|
|
#include "frontends/FontMetrics.h"
|
|
|
|
#include "frontends/Painter.h"
|
2003-03-04 09:27:27 +00:00
|
|
|
|
2007-11-27 23:09:06 +00:00
|
|
|
#include <ostream>
|
2006-10-26 19:00:28 +00:00
|
|
|
|
2007-12-12 19:28:07 +00:00
|
|
|
using namespace std;
|
|
|
|
|
2007-07-17 17:40:44 +00:00
|
|
|
namespace lyx {
|
|
|
|
|
2016-05-22 21:48:28 +00:00
|
|
|
using frontend::Painter;
|
|
|
|
using frontend::FontMetrics;
|
|
|
|
|
2006-10-26 19:00:28 +00:00
|
|
|
/*
|
|
|
|
* Class Change has a changetime field that specifies the exact time at which
|
|
|
|
* a specific change was made. The change time is used as a guidance for the
|
|
|
|
* user while editing his document. Presently, it is not considered for LaTeX
|
2006-10-29 21:48:23 +00:00
|
|
|
* export.
|
|
|
|
* When merging two adjacent changes, the changetime is not considered,
|
|
|
|
* only the equality of the change type and author is checked (in method
|
|
|
|
* isSimilarTo(...)). If two changes are in fact merged (in method merge()),
|
2007-05-28 22:27:45 +00:00
|
|
|
* the later change time is preserved.
|
2006-10-26 19:00:28 +00:00
|
|
|
*/
|
2003-09-09 17:25:35 +00:00
|
|
|
|
2008-09-22 15:30:26 +00:00
|
|
|
bool Change::isSimilarTo(Change const & change) const
|
2003-02-08 19:18:01 +00:00
|
|
|
{
|
2007-11-01 22:17:22 +00:00
|
|
|
if (type != change.type)
|
2006-10-26 19:00:28 +00:00
|
|
|
return false;
|
|
|
|
|
2007-11-01 22:17:22 +00:00
|
|
|
if (type == Change::UNCHANGED)
|
2006-10-26 19:00:28 +00:00
|
|
|
return true;
|
|
|
|
|
2006-10-29 21:48:23 +00:00
|
|
|
return author == change.author;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-02-09 20:47:32 +00:00
|
|
|
Color Change::color() const
|
2008-09-22 15:30:26 +00:00
|
|
|
{
|
2009-02-09 20:47:32 +00:00
|
|
|
Color color = Color_none;
|
2008-09-22 15:30:26 +00:00
|
|
|
switch (author % 5) {
|
|
|
|
case 0:
|
2019-12-24 07:37:27 +00:00
|
|
|
color = Color_addedtextauthor1;
|
2008-09-22 15:30:26 +00:00
|
|
|
break;
|
|
|
|
case 1:
|
2019-12-24 07:37:27 +00:00
|
|
|
color = Color_addedtextauthor2;
|
2008-09-22 15:30:26 +00:00
|
|
|
break;
|
|
|
|
case 2:
|
2019-12-24 07:37:27 +00:00
|
|
|
color = Color_addedtextauthor3;
|
2008-09-22 15:30:26 +00:00
|
|
|
break;
|
|
|
|
case 3:
|
2019-12-24 07:37:27 +00:00
|
|
|
color = Color_addedtextauthor4;
|
2008-09-22 15:30:26 +00:00
|
|
|
break;
|
|
|
|
case 4:
|
2019-12-24 07:37:27 +00:00
|
|
|
color = Color_addedtextauthor5;
|
2008-09-22 15:30:26 +00:00
|
|
|
break;
|
|
|
|
}
|
2009-02-09 20:47:32 +00:00
|
|
|
|
|
|
|
if (deleted())
|
|
|
|
color.mergeColor = Color_deletedtextmodifier;
|
|
|
|
|
2008-09-22 15:30:26 +00:00
|
|
|
return color;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-10-29 21:48:23 +00:00
|
|
|
bool operator==(Change const & l, Change const & r)
|
|
|
|
{
|
2007-11-01 22:17:22 +00:00
|
|
|
if (l.type != r.type)
|
2007-01-08 23:28:41 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
// two changes of type UNCHANGED are always equal
|
2007-11-01 22:17:22 +00:00
|
|
|
if (l.type == Change::UNCHANGED)
|
2007-01-08 23:28:41 +00:00
|
|
|
return true;
|
2007-05-28 22:27:45 +00:00
|
|
|
|
2007-11-01 22:17:22 +00:00
|
|
|
return l.author == r.author && l.changetime == r.changetime;
|
2003-02-08 19:18:01 +00:00
|
|
|
}
|
2003-03-04 09:27:27 +00:00
|
|
|
|
2003-02-08 19:18:01 +00:00
|
|
|
|
|
|
|
bool operator!=(Change const & l, Change const & r)
|
|
|
|
{
|
|
|
|
return !(l == r);
|
|
|
|
}
|
|
|
|
|
2003-03-04 09:27:27 +00:00
|
|
|
|
2003-02-08 19:18:01 +00:00
|
|
|
bool operator==(Changes::Range const & r1, Changes::Range const & r2)
|
|
|
|
{
|
2003-03-04 09:27:27 +00:00
|
|
|
return r1.start == r2.start && r1.end == r2.end;
|
2003-02-08 19:18:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool operator!=(Changes::Range const & r1, Changes::Range const & r2)
|
|
|
|
{
|
|
|
|
return !(r1 == r2);
|
|
|
|
}
|
2003-03-04 09:27:27 +00:00
|
|
|
|
|
|
|
|
2003-02-08 19:18:01 +00:00
|
|
|
bool Changes::Range::intersects(Range const & r) const
|
|
|
|
{
|
2006-10-20 14:31:54 +00:00
|
|
|
return r.start < end && r.end > start; // end itself is not in the range!
|
2003-02-08 19:18:01 +00:00
|
|
|
}
|
2003-03-04 09:27:27 +00:00
|
|
|
|
|
|
|
|
2006-10-19 07:12:48 +00:00
|
|
|
void Changes::set(Change const & change, pos_type const pos)
|
2003-02-08 19:18:01 +00:00
|
|
|
{
|
|
|
|
set(change, pos, pos + 1);
|
|
|
|
}
|
|
|
|
|
2003-03-04 09:27:27 +00:00
|
|
|
|
2006-10-21 14:34:05 +00:00
|
|
|
void Changes::set(Change const & change, pos_type const start, pos_type const end)
|
2003-02-08 19:18:01 +00:00
|
|
|
{
|
2007-04-01 15:09:08 +00:00
|
|
|
if (change.type != Change::UNCHANGED) {
|
2007-11-15 20:04:51 +00:00
|
|
|
LYXERR(Debug::CHANGES, "setting change (type: " << change.type
|
2017-07-03 17:53:14 +00:00
|
|
|
<< ", author: " << change.author
|
2007-11-29 09:00:28 +00:00
|
|
|
<< ", time: " << long(change.changetime)
|
2007-11-15 20:04:51 +00:00
|
|
|
<< ") in range (" << start << ", " << end << ")");
|
2003-03-12 02:34:46 +00:00
|
|
|
}
|
2003-03-04 09:27:27 +00:00
|
|
|
|
2006-10-21 14:34:05 +00:00
|
|
|
Range const newRange(start, end);
|
2003-03-04 09:27:27 +00:00
|
|
|
|
2006-10-21 14:34:05 +00:00
|
|
|
ChangeTable::iterator it = table_.begin();
|
2003-02-08 19:18:01 +00:00
|
|
|
|
2006-10-21 14:34:05 +00:00
|
|
|
for (; it != table_.end(); ) {
|
|
|
|
// current change starts like or follows new change
|
|
|
|
if (it->range.start >= start) {
|
2003-02-08 19:18:01 +00:00
|
|
|
break;
|
2006-10-21 14:34:05 +00:00
|
|
|
}
|
2003-02-08 19:18:01 +00:00
|
|
|
|
2006-10-21 14:34:05 +00:00
|
|
|
// new change intersects with existing change
|
|
|
|
if (it->range.end > start) {
|
|
|
|
pos_type oldEnd = it->range.end;
|
|
|
|
it->range.end = start;
|
2007-04-01 15:09:08 +00:00
|
|
|
|
2007-11-15 20:04:51 +00:00
|
|
|
LYXERR(Debug::CHANGES, " cutting tail of type " << it->change.type
|
2007-04-01 15:09:08 +00:00
|
|
|
<< " resulting in range (" << it->range.start << ", "
|
2007-11-15 20:04:51 +00:00
|
|
|
<< it->range.end << ")");
|
2007-04-01 15:09:08 +00:00
|
|
|
|
2006-10-21 14:34:05 +00:00
|
|
|
++it;
|
|
|
|
if (oldEnd >= end) {
|
2007-11-15 20:04:51 +00:00
|
|
|
LYXERR(Debug::CHANGES, " inserting tail in range ("
|
|
|
|
<< end << ", " << oldEnd << ")");
|
2006-10-21 14:34:05 +00:00
|
|
|
it = table_.insert(it, ChangeRange((it-1)->change, Range(end, oldEnd)));
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
2003-03-04 09:27:27 +00:00
|
|
|
|
2006-10-21 14:34:05 +00:00
|
|
|
++it;
|
2003-03-12 02:34:46 +00:00
|
|
|
}
|
2003-03-04 09:27:27 +00:00
|
|
|
|
2006-10-21 14:34:05 +00:00
|
|
|
if (change.type != Change::UNCHANGED) {
|
2007-11-15 20:04:51 +00:00
|
|
|
LYXERR(Debug::CHANGES, " inserting change");
|
2006-10-21 14:34:05 +00:00
|
|
|
it = table_.insert(it, ChangeRange(change, Range(start, end)));
|
2003-02-08 19:18:01 +00:00
|
|
|
++it;
|
|
|
|
}
|
|
|
|
|
2006-10-21 14:34:05 +00:00
|
|
|
for (; it != table_.end(); ) {
|
|
|
|
// new change 'contains' existing change
|
|
|
|
if (newRange.contains(it->range)) {
|
2007-11-15 20:04:51 +00:00
|
|
|
LYXERR(Debug::CHANGES, " removing subrange ("
|
|
|
|
<< it->range.start << ", " << it->range.end << ")");
|
2006-10-21 14:34:05 +00:00
|
|
|
it = table_.erase(it);
|
|
|
|
continue;
|
|
|
|
}
|
2003-03-04 09:27:27 +00:00
|
|
|
|
2006-10-21 14:34:05 +00:00
|
|
|
// new change precedes existing change
|
2007-11-01 22:17:22 +00:00
|
|
|
if (it->range.start >= end)
|
2006-10-21 14:34:05 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// new change intersects with existing change
|
|
|
|
it->range.start = end;
|
2007-11-15 20:04:51 +00:00
|
|
|
LYXERR(Debug::CHANGES, " cutting head of type "
|
2007-04-01 15:09:08 +00:00
|
|
|
<< it->change.type << " resulting in range ("
|
2007-11-15 20:04:51 +00:00
|
|
|
<< end << ", " << it->range.end << ")");
|
2006-10-21 14:34:05 +00:00
|
|
|
break; // no need for another iteration
|
2003-02-08 19:18:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
merge();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-01-05 20:21:27 +00:00
|
|
|
void Changes::erase(pos_type const pos)
|
2003-02-08 19:18:01 +00:00
|
|
|
{
|
2007-11-15 20:04:51 +00:00
|
|
|
LYXERR(Debug::CHANGES, "Erasing change at position " << pos);
|
2006-10-21 15:36:04 +00:00
|
|
|
|
2017-01-11 22:36:20 +00:00
|
|
|
for (ChangeRange & cr : table_) {
|
2006-10-21 15:36:04 +00:00
|
|
|
// range (pos,pos+x) becomes (pos,pos+x-1)
|
2017-01-11 22:36:20 +00:00
|
|
|
if (cr.range.start > pos)
|
|
|
|
--(cr.range.start);
|
2006-10-21 15:36:04 +00:00
|
|
|
// range (pos-x,pos) stays (pos-x,pos)
|
2017-01-11 22:36:20 +00:00
|
|
|
if (cr.range.end > pos)
|
|
|
|
--(cr.range.end);
|
2006-10-21 15:36:04 +00:00
|
|
|
}
|
2003-03-04 09:27:27 +00:00
|
|
|
|
2006-10-21 15:36:04 +00:00
|
|
|
merge();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Changes::insert(Change const & change, lyx::pos_type pos)
|
|
|
|
{
|
2007-04-01 15:09:08 +00:00
|
|
|
if (change.type != Change::UNCHANGED) {
|
2007-11-15 20:04:51 +00:00
|
|
|
LYXERR(Debug::CHANGES, "Inserting change of type " << change.type
|
|
|
|
<< " at position " << pos);
|
2006-10-21 15:36:04 +00:00
|
|
|
}
|
|
|
|
|
2017-01-11 22:36:20 +00:00
|
|
|
for (ChangeRange & cr : table_) {
|
2006-10-21 15:36:04 +00:00
|
|
|
// range (pos,pos+x) becomes (pos+1,pos+x+1)
|
2017-01-11 22:36:20 +00:00
|
|
|
if (cr.range.start >= pos)
|
|
|
|
++(cr.range.start);
|
2003-02-08 19:18:01 +00:00
|
|
|
|
2006-10-21 15:36:04 +00:00
|
|
|
// range (pos-x,pos) stays as it is
|
2017-01-11 22:36:20 +00:00
|
|
|
if (cr.range.end > pos)
|
|
|
|
++(cr.range.end);
|
2003-02-08 19:18:01 +00:00
|
|
|
}
|
2006-10-21 15:36:04 +00:00
|
|
|
|
|
|
|
set(change, pos, pos + 1); // set will call merge
|
2003-02-08 19:18:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-01-07 18:13:25 +00:00
|
|
|
Change const & Changes::lookup(pos_type const pos) const
|
2003-02-08 19:18:01 +00:00
|
|
|
{
|
2007-01-07 18:13:25 +00:00
|
|
|
static Change const noChange = Change(Change::UNCHANGED);
|
2017-01-11 22:36:20 +00:00
|
|
|
for (ChangeRange const & cr : table_)
|
|
|
|
if (cr.range.contains(pos))
|
|
|
|
return cr.change;
|
2007-01-07 18:13:25 +00:00
|
|
|
return noChange;
|
2003-02-08 19:18:01 +00:00
|
|
|
}
|
2003-03-04 09:27:27 +00:00
|
|
|
|
2003-02-08 19:18:01 +00:00
|
|
|
|
2009-08-04 22:50:36 +00:00
|
|
|
bool Changes::isDeleted(pos_type start, pos_type end) const
|
2009-05-14 22:21:05 +00:00
|
|
|
{
|
2017-01-11 22:36:20 +00:00
|
|
|
for (ChangeRange const & cr : table_)
|
|
|
|
if (cr.range.contains(Range(start, end))) {
|
2009-05-14 22:21:05 +00:00
|
|
|
LYXERR(Debug::CHANGES, "range ("
|
|
|
|
<< start << ", " << end << ") fully contains ("
|
2017-01-11 22:36:20 +00:00
|
|
|
<< cr.range.start << ", " << cr.range.end
|
|
|
|
<< ") of type " << cr.change.type);
|
|
|
|
return cr.change.type == Change::DELETED;
|
2009-05-14 22:21:05 +00:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-10-20 15:27:11 +00:00
|
|
|
bool Changes::isChanged(pos_type const start, pos_type const end) const
|
2003-02-08 19:18:01 +00:00
|
|
|
{
|
2017-01-11 22:36:20 +00:00
|
|
|
for (ChangeRange const & cr : table_)
|
|
|
|
if (cr.range.intersects(Range(start, end))) {
|
2007-11-15 20:04:51 +00:00
|
|
|
LYXERR(Debug::CHANGES, "found intersection of range ("
|
2007-04-01 15:09:08 +00:00
|
|
|
<< start << ", " << end << ") with ("
|
2017-01-11 22:36:20 +00:00
|
|
|
<< cr.range.start << ", " << cr.range.end
|
|
|
|
<< ") of type " << cr.change.type);
|
2003-02-08 19:18:01 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2003-03-04 09:27:27 +00:00
|
|
|
|
2016-01-30 23:14:36 +00:00
|
|
|
bool Changes::isChanged() const
|
|
|
|
{
|
2017-01-11 22:36:20 +00:00
|
|
|
for (ChangeRange const & cr : table_)
|
|
|
|
if (cr.change.changed())
|
2016-01-30 23:14:36 +00:00
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-02-08 19:18:01 +00:00
|
|
|
void Changes::merge()
|
|
|
|
{
|
|
|
|
ChangeTable::iterator it = table_.begin();
|
|
|
|
|
|
|
|
while (it != table_.end()) {
|
2007-11-15 20:04:51 +00:00
|
|
|
LYXERR(Debug::CHANGES, "found change of type " << it->change.type
|
2007-04-01 15:09:08 +00:00
|
|
|
<< " and range (" << it->range.start << ", " << it->range.end
|
2007-11-15 20:04:51 +00:00
|
|
|
<< ")");
|
2003-03-04 09:27:27 +00:00
|
|
|
|
2003-02-08 19:18:01 +00:00
|
|
|
if (it->range.start == it->range.end) {
|
2007-11-15 20:04:51 +00:00
|
|
|
LYXERR(Debug::CHANGES, "removing empty range for pos "
|
|
|
|
<< it->range.start);
|
2003-03-12 02:34:46 +00:00
|
|
|
|
2003-02-08 19:18:01 +00:00
|
|
|
table_.erase(it);
|
|
|
|
// start again
|
|
|
|
it = table_.begin();
|
|
|
|
continue;
|
|
|
|
}
|
2003-03-04 09:27:27 +00:00
|
|
|
|
2003-02-08 19:18:01 +00:00
|
|
|
if (it + 1 == table_.end())
|
|
|
|
break;
|
2003-03-04 09:27:27 +00:00
|
|
|
|
2007-11-15 20:04:51 +00:00
|
|
|
if (it->change.isSimilarTo((it + 1)->change)
|
|
|
|
&& it->range.end == (it + 1)->range.start) {
|
|
|
|
LYXERR(Debug::CHANGES, "merging ranges (" << it->range.start << ", "
|
2007-04-01 15:09:08 +00:00
|
|
|
<< it->range.end << ") and (" << (it + 1)->range.start << ", "
|
2007-11-15 20:04:51 +00:00
|
|
|
<< (it + 1)->range.end << ")");
|
2007-04-01 15:09:08 +00:00
|
|
|
|
2003-02-08 19:18:01 +00:00
|
|
|
(it + 1)->range.start = it->range.start;
|
2007-12-12 19:28:07 +00:00
|
|
|
(it + 1)->change.changetime = max(it->change.changetime,
|
2007-05-28 22:27:45 +00:00
|
|
|
(it + 1)->change.changetime);
|
2003-02-08 19:18:01 +00:00
|
|
|
table_.erase(it);
|
|
|
|
// start again
|
|
|
|
it = table_.begin();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-04 09:27:27 +00:00
|
|
|
|
2009-10-12 16:22:05 +00:00
|
|
|
namespace {
|
2012-10-27 13:45:27 +00:00
|
|
|
|
2019-12-24 13:31:08 +00:00
|
|
|
docstring getLaTeXMarkup(docstring const & macro, Author const & author,
|
2009-10-13 07:30:46 +00:00
|
|
|
docstring const & chgTime,
|
2009-10-12 16:22:05 +00:00
|
|
|
OutputParams const & runparams)
|
|
|
|
{
|
|
|
|
if (macro.empty())
|
|
|
|
return docstring();
|
|
|
|
|
2014-07-05 12:49:51 +00:00
|
|
|
docstring uncodable_author;
|
2009-10-12 16:22:05 +00:00
|
|
|
odocstringstream ods;
|
|
|
|
|
2019-12-24 13:31:08 +00:00
|
|
|
docstring const author_name = author.name();
|
|
|
|
docstring const author_initials = author.initials();
|
|
|
|
|
2009-10-12 16:22:05 +00:00
|
|
|
ods << macro;
|
2019-12-24 13:31:08 +00:00
|
|
|
if (!author_initials.empty()) {
|
|
|
|
docstring uncodable_initials;
|
|
|
|
// convert utf8 author initials to something representable
|
|
|
|
// in the current encoding
|
|
|
|
pair<docstring, docstring> author_initials_latexed =
|
|
|
|
runparams.encoding->latexString(author_initials, runparams.dryrun);
|
|
|
|
if (!author_initials_latexed.second.empty()) {
|
|
|
|
LYXERR0("Omitting uncodable characters '"
|
|
|
|
<< author_initials_latexed.second
|
|
|
|
<< "' in change author initials!");
|
|
|
|
uncodable_initials = author_initials;
|
|
|
|
}
|
|
|
|
ods << "[" << author_initials_latexed.first << "]";
|
|
|
|
// warn user (once) if we found uncodable glyphs.
|
|
|
|
if (!uncodable_initials.empty()) {
|
|
|
|
static std::set<docstring> warned_author_initials;
|
|
|
|
static Mutex warned_mutex;
|
|
|
|
Mutex::Locker locker(&warned_mutex);
|
|
|
|
if (warned_author_initials.find(uncodable_initials) == warned_author_initials.end()) {
|
|
|
|
frontend::Alert::warning(_("Uncodable character in author initials"),
|
|
|
|
support::bformat(_("The author initials '%1$s',\n"
|
|
|
|
"used for change tracking, contain the following glyphs that\n"
|
|
|
|
"cannot be represented in the current encoding: %2$s.\n"
|
|
|
|
"These glyphs will be omitted in the exported LaTeX file.\n\n"
|
|
|
|
"Choose an appropriate document encoding (such as utf8)\n"
|
|
|
|
"or change the author initials."),
|
|
|
|
uncodable_initials, author_initials_latexed.second));
|
2020-01-03 12:39:50 +00:00
|
|
|
warned_author_initials.insert(uncodable_initials);
|
2019-12-24 13:31:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-10-12 16:22:05 +00:00
|
|
|
// convert utf8 author name to something representable
|
|
|
|
// in the current encoding
|
2012-06-30 11:30:48 +00:00
|
|
|
pair<docstring, docstring> author_latexed =
|
2019-12-24 13:31:08 +00:00
|
|
|
runparams.encoding->latexString(author_name, runparams.dryrun);
|
2012-06-30 11:30:48 +00:00
|
|
|
if (!author_latexed.second.empty()) {
|
|
|
|
LYXERR0("Omitting uncodable characters '"
|
|
|
|
<< author_latexed.second
|
|
|
|
<< "' in change author name!");
|
2019-12-24 13:31:08 +00:00
|
|
|
uncodable_author = author_name;
|
2009-10-12 16:22:05 +00:00
|
|
|
}
|
2019-12-24 13:31:08 +00:00
|
|
|
ods << "{" << author_latexed.first << "}{" << chgTime << "}{";
|
2009-10-12 16:22:05 +00:00
|
|
|
|
|
|
|
// warn user (once) if we found uncodable glyphs.
|
2014-07-05 12:49:51 +00:00
|
|
|
if (!uncodable_author.empty()) {
|
|
|
|
static std::set<docstring> warned_authors;
|
|
|
|
static Mutex warned_mutex;
|
|
|
|
Mutex::Locker locker(&warned_mutex);
|
|
|
|
if (warned_authors.find(uncodable_author) == warned_authors.end()) {
|
|
|
|
frontend::Alert::warning(_("Uncodable character in author name"),
|
2009-10-12 16:22:05 +00:00
|
|
|
support::bformat(_("The author name '%1$s',\n"
|
2012-06-30 11:30:48 +00:00
|
|
|
"used for change tracking, contains the following glyphs that\n"
|
|
|
|
"cannot be represented in the current encoding: %2$s.\n"
|
|
|
|
"These glyphs will be omitted in the exported LaTeX file.\n\n"
|
2010-03-10 15:09:44 +00:00
|
|
|
"Choose an appropriate document encoding (such as utf8)\n"
|
2012-06-30 11:30:48 +00:00
|
|
|
"or change the spelling of the author name."),
|
|
|
|
uncodable_author, author_latexed.second));
|
2014-07-05 12:49:51 +00:00
|
|
|
warned_authors.insert(uncodable_author);
|
|
|
|
}
|
2009-10-12 16:22:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ods.str();
|
|
|
|
}
|
2012-10-27 13:45:27 +00:00
|
|
|
|
2017-07-23 11:11:54 +00:00
|
|
|
} // namespace
|
2009-10-12 16:22:05 +00:00
|
|
|
|
|
|
|
|
Introduce a wrapper class for odocstream to help ensuring that no
blank lines may be inadvertently output. This is achieved by using two
special iomanip-like variables (breakln and safebreakln) in the lyx::
namespace. When they are inserted in the stream, a newline is output
only if not already at the beginning of a line. The difference between
breakln and safebreakln is that, if needed, the former outputs '\n'
and the latter "%\n".
In future, the new class will also be used for counting the number of
newlines issued. Even if the infractrure for doing that is already in
place, the counting is essentially still done the old way.
There are still places in the code where the functionality of the
class could be used, most probably. ATM, it is used for InsetTabular,
InsetListings, InsetFloat, and InsetText.
The Comment and GreyedOut insets required a special treatment and a
new InsetLayout parameter (Display) has been introduced. The default
for Display is "true", meaning that the corresponding latex
environment is of "display" type, i.e., it stands on its own, whereas
"false" means that the contents appear inline with the text. The
latter is the case for both Comment and GreyedOut insets.
Mostly, the only visible effects on latex exports should be the
disappearing of some redundant % chars and the appearing/disappearing
of null {} latex groups after a comment or lyxgreyedout environments
(they are related to the presence or absence of a space immediately
after those environments), as well as the fact that math environments
are now started on their own lines.
As a last thing, only the latex code between \begin{document} and
\end{document} goes through the new class, the preamble being directly
output through odocstream, as usual.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@37360 a592a061-630c-0410-9148-cb99ea01b6c8
2011-01-29 02:41:13 +00:00
|
|
|
int Changes::latexMarkChange(otexstream & os, BufferParams const & bparams,
|
2009-10-12 16:22:05 +00:00
|
|
|
Change const & oldChange, Change const & change,
|
|
|
|
OutputParams const & runparams)
|
2003-03-04 09:27:27 +00:00
|
|
|
{
|
2014-03-29 22:52:36 +00:00
|
|
|
if (!bparams.output_changes || oldChange == change)
|
2003-02-08 19:18:01 +00:00
|
|
|
return 0;
|
2005-04-26 11:12:20 +00:00
|
|
|
|
2003-02-08 19:18:01 +00:00
|
|
|
int column = 0;
|
2003-03-04 09:27:27 +00:00
|
|
|
|
2007-05-08 17:46:03 +00:00
|
|
|
if (oldChange.type != Change::UNCHANGED) {
|
2019-12-28 12:43:17 +00:00
|
|
|
if (oldChange.type != Change::DELETED || runparams.ctObject != OutputParams::CT_OMITOBJECT) {
|
|
|
|
// close \lyxadded or \lyxdeleted
|
|
|
|
os << '}';
|
|
|
|
column++;
|
|
|
|
}
|
2017-10-22 11:12:33 +00:00
|
|
|
if (oldChange.type == Change::DELETED
|
2020-01-13 07:59:26 +00:00
|
|
|
&& !runparams.wasDisplayMath)
|
2014-03-22 11:27:46 +00:00
|
|
|
--runparams.inulemcmd;
|
2003-02-08 19:18:01 +00:00
|
|
|
}
|
2003-03-04 09:27:27 +00:00
|
|
|
|
2007-05-08 17:46:03 +00:00
|
|
|
docstring chgTime;
|
2013-01-07 14:46:47 +00:00
|
|
|
chgTime += asctime(gmtime(&change.changetime));
|
2009-10-12 16:22:05 +00:00
|
|
|
// remove trailing '\n'
|
|
|
|
chgTime.erase(chgTime.end() - 1);
|
|
|
|
|
|
|
|
docstring macro_beg;
|
2014-03-22 11:27:46 +00:00
|
|
|
if (change.type == Change::DELETED) {
|
2019-12-28 12:43:17 +00:00
|
|
|
if (runparams.ctObject == OutputParams::CT_OMITOBJECT)
|
|
|
|
return 0;
|
|
|
|
else if (runparams.ctObject == OutputParams::CT_OBJECT)
|
|
|
|
macro_beg = from_ascii("\\lyxobjdeleted");
|
|
|
|
else if (runparams.ctObject == OutputParams::CT_DISPLAYOBJECT)
|
|
|
|
macro_beg = from_ascii("\\lyxdisplayobjdeleted");
|
|
|
|
else if (runparams.ctObject == OutputParams::CT_UDISPLAYOBJECT)
|
|
|
|
macro_beg = from_ascii("\\lyxudisplayobjdeleted");
|
|
|
|
else {
|
|
|
|
macro_beg = from_ascii("\\lyxdeleted");
|
2020-01-13 07:59:26 +00:00
|
|
|
if (!runparams.inDisplayMath)
|
2019-12-28 12:43:17 +00:00
|
|
|
++runparams.inulemcmd;
|
|
|
|
}
|
2014-03-22 11:27:46 +00:00
|
|
|
}
|
2009-10-12 16:22:05 +00:00
|
|
|
else if (change.type == Change::INSERTED)
|
2019-12-24 13:31:08 +00:00
|
|
|
macro_beg = from_ascii("\\lyxadded");
|
2017-07-03 17:53:14 +00:00
|
|
|
|
2009-10-12 16:22:05 +00:00
|
|
|
docstring str = getLaTeXMarkup(macro_beg,
|
2019-12-24 13:31:08 +00:00
|
|
|
bparams.authors().get(change.author),
|
2009-10-12 16:22:05 +00:00
|
|
|
chgTime, runparams);
|
2017-07-03 17:53:14 +00:00
|
|
|
|
2009-10-12 16:22:05 +00:00
|
|
|
os << str;
|
|
|
|
column += str.size();
|
2003-03-04 09:27:27 +00:00
|
|
|
|
2003-02-08 19:18:01 +00:00
|
|
|
return column;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-07-23 20:08:05 +00:00
|
|
|
void Changes::lyxMarkChange(ostream & os, BufferParams const & bparams, int & column,
|
2005-01-05 20:21:27 +00:00
|
|
|
Change const & old, Change const & change)
|
2003-02-08 19:18:01 +00:00
|
|
|
{
|
|
|
|
if (old == change)
|
|
|
|
return;
|
|
|
|
|
|
|
|
column = 0;
|
2003-03-04 09:27:27 +00:00
|
|
|
|
2020-11-02 22:09:13 +00:00
|
|
|
int const buffer_id = bparams.authors().get(change.author).bufferId();
|
2009-07-23 20:08:05 +00:00
|
|
|
|
2003-02-08 19:18:01 +00:00
|
|
|
switch (change.type) {
|
|
|
|
case Change::UNCHANGED:
|
|
|
|
os << "\n\\change_unchanged\n";
|
|
|
|
break;
|
|
|
|
|
2010-11-17 16:13:59 +00:00
|
|
|
case Change::DELETED:
|
2009-07-23 20:08:05 +00:00
|
|
|
os << "\n\\change_deleted " << buffer_id
|
2006-10-26 19:00:28 +00:00
|
|
|
<< " " << change.changetime << "\n";
|
2003-02-08 19:18:01 +00:00
|
|
|
break;
|
|
|
|
|
2010-11-17 16:13:59 +00:00
|
|
|
case Change::INSERTED:
|
2009-07-23 20:08:05 +00:00
|
|
|
os << "\n\\change_inserted " << buffer_id
|
2006-10-26 19:00:28 +00:00
|
|
|
<< " " << change.changetime << "\n";
|
2003-02-08 19:18:01 +00:00
|
|
|
break;
|
2005-01-05 20:21:27 +00:00
|
|
|
}
|
2003-02-08 19:18:01 +00:00
|
|
|
}
|
2006-10-21 00:16:43 +00:00
|
|
|
|
|
|
|
|
2020-11-01 22:37:19 +00:00
|
|
|
void Changes::checkAuthors(AuthorList const & authorList) const
|
2007-07-09 20:52:34 +00:00
|
|
|
{
|
2017-01-11 22:36:20 +00:00
|
|
|
for (ChangeRange const & cr : table_)
|
|
|
|
if (cr.change.type != Change::UNCHANGED)
|
|
|
|
authorList.get(cr.change.author).setUsed(true);
|
2007-07-09 20:52:34 +00:00
|
|
|
}
|
|
|
|
|
2008-09-30 11:06:34 +00:00
|
|
|
|
2013-03-08 21:18:26 +00:00
|
|
|
void Changes::addToToc(DocIterator const & cdit, Buffer const & buffer,
|
2017-01-13 10:06:48 +00:00
|
|
|
bool output_active, TocBackend & backend) const
|
2008-09-30 11:06:34 +00:00
|
|
|
{
|
|
|
|
if (table_.empty())
|
|
|
|
return;
|
|
|
|
|
2017-01-13 10:06:48 +00:00
|
|
|
shared_ptr<Toc> change_list = backend.toc("change");
|
2008-09-30 11:06:34 +00:00
|
|
|
AuthorList const & author_list = buffer.params().authors();
|
|
|
|
DocIterator dit = cdit;
|
|
|
|
|
2017-01-11 22:36:20 +00:00
|
|
|
for (ChangeRange const & cr : table_) {
|
2008-09-30 11:06:34 +00:00
|
|
|
docstring str;
|
2017-01-11 22:36:20 +00:00
|
|
|
switch (cr.change.type) {
|
2008-09-30 11:06:34 +00:00
|
|
|
case Change::UNCHANGED:
|
|
|
|
continue;
|
|
|
|
case Change::DELETED:
|
2015-12-02 21:56:10 +00:00
|
|
|
// ✂ U+2702 BLACK SCISSORS
|
2008-09-30 11:06:34 +00:00
|
|
|
str.push_back(0x2702);
|
|
|
|
break;
|
|
|
|
case Change::INSERTED:
|
2015-12-02 21:56:10 +00:00
|
|
|
// ✍ U+270D WRITING HAND
|
|
|
|
str.push_back(0x270d);
|
|
|
|
break;
|
2008-09-30 11:06:34 +00:00
|
|
|
}
|
2017-01-11 22:36:20 +00:00
|
|
|
dit.pos() = cr.range.start;
|
2008-10-03 19:53:53 +00:00
|
|
|
Paragraph const & par = dit.paragraph();
|
2017-01-11 22:36:20 +00:00
|
|
|
str += " " + par.asString(cr.range.start, min(par.size(), cr.range.end));
|
|
|
|
if (cr.range.end > par.size())
|
2015-12-02 21:56:10 +00:00
|
|
|
// ¶ U+00B6 PILCROW SIGN
|
|
|
|
str.push_back(0xb6);
|
2017-01-11 22:36:20 +00:00
|
|
|
docstring const & author = author_list.get(cr.change.author).name();
|
2016-01-08 19:06:50 +00:00
|
|
|
Toc::iterator it = TocBackend::findItem(*change_list, 0, author);
|
2015-09-01 16:08:35 +00:00
|
|
|
if (it == change_list->end()) {
|
2015-12-02 21:56:10 +00:00
|
|
|
change_list->push_back(TocItem(dit, 0, author, true));
|
2016-06-06 19:02:49 +00:00
|
|
|
change_list->push_back(TocItem(dit, 1, str, output_active));
|
2008-09-30 11:26:40 +00:00
|
|
|
continue;
|
|
|
|
}
|
2015-09-01 16:08:35 +00:00
|
|
|
for (++it; it != change_list->end(); ++it) {
|
2008-09-30 11:26:40 +00:00
|
|
|
if (it->depth() == 0 && it->str() != author)
|
|
|
|
break;
|
2008-09-30 11:06:34 +00:00
|
|
|
}
|
2016-06-06 19:02:49 +00:00
|
|
|
change_list->insert(it, TocItem(dit, 1, str, output_active));
|
2008-09-30 11:06:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-30 23:14:36 +00:00
|
|
|
|
2016-05-22 21:48:28 +00:00
|
|
|
void Change::paintCue(PainterInfo & pi, double const x1, double const y,
|
|
|
|
double const x2, FontInfo const & font) const
|
|
|
|
{
|
2019-12-27 09:35:52 +00:00
|
|
|
if (!changed() || (!lyxrc.ct_additions_underlined && inserted()))
|
2016-05-22 21:48:28 +00:00
|
|
|
return;
|
|
|
|
// Calculate 1/3 height of font
|
|
|
|
FontMetrics const & fm = theFontMetrics(font);
|
2016-05-23 10:38:48 +00:00
|
|
|
double const y_bar = deleted() ? y - fm.maxAscent() / 3
|
|
|
|
: y + 2 * pi.base.solidLineOffset() + pi.base.solidLineThickness();
|
|
|
|
pi.pain.line(int(x1), int(y_bar), int(x2), int(y_bar), color(),
|
2016-05-22 21:48:28 +00:00
|
|
|
Painter::line_solid, pi.base.solidLineThickness());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Change::paintCue(PainterInfo & pi, double const x1, double const y1,
|
|
|
|
double const x2, double const y2) const
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* y1 /
|
|
|
|
* /
|
|
|
|
* /
|
|
|
|
* /
|
|
|
|
* /
|
|
|
|
* y2 /_____
|
|
|
|
* x1 x2
|
|
|
|
*/
|
|
|
|
switch(type) {
|
|
|
|
case UNCHANGED:
|
|
|
|
return;
|
2019-12-27 09:35:52 +00:00
|
|
|
case INSERTED: {
|
|
|
|
if (!lyxrc.ct_additions_underlined)
|
2020-05-17 11:57:31 +00:00
|
|
|
return;
|
2016-05-23 14:06:26 +00:00
|
|
|
pi.pain.line(int(x1), int(y2) + 1, int(x2), int(y2) + 1,
|
|
|
|
color(), Painter::line_solid,
|
|
|
|
pi.base.solidLineThickness());
|
|
|
|
return;
|
2019-12-27 09:35:52 +00:00
|
|
|
}
|
2016-05-22 21:48:28 +00:00
|
|
|
case DELETED:
|
2016-05-23 14:06:26 +00:00
|
|
|
// FIXME: we cannot use antialias since we keep drawing on the same
|
|
|
|
// background with the current painting mechanism.
|
|
|
|
pi.pain.line(int(x1), int(y2), int(x2), int(y1),
|
|
|
|
color(), Painter::line_solid_aliased,
|
|
|
|
pi.base.solidLineThickness());
|
|
|
|
return;
|
2016-05-22 21:48:28 +00:00
|
|
|
}
|
|
|
|
}
|
2016-01-30 23:14:36 +00:00
|
|
|
|
|
|
|
|
2006-10-21 00:16:43 +00:00
|
|
|
} // namespace lyx
|