+2003-02-08 John Levon <levon@movementarian.org>
+
+ * chkconfig.ltx: look for dvipost package
+
+ * configure.m4: look for and prefer pplatex
+
+ * ui/default.ui: Add change tracking menu items
+
2003-02-07 Tomasz Luczak <tlu@technodat.com.pl>
* kbd/polski.kmap: new keymap, which assumes that you have a
\TestPackage{varioref}
\TestPackage{prettyref}
\TestPackage{natbib}
+\TestPackage{dvipost}
% The test for the graphics package is slightly more involved...
\newcommand\groption{dvips}
rm -f chklatex.ltx chklatex.log])dnl
dnl
# Search LaTeX2e
-SEARCH_PROG([for a LaTeX2e program],LATEX,latex latex2e,CHECKLATEX2E,dnl
+SEARCH_PROG([for a LaTeX2e program],LATEX,pplatex latex2e latex,CHECKLATEX2E,dnl
[lyx_check_config=no])
latex_to_dvi=$LATEX
test -z "$latex_to_dvi" && latex_to_dvi="none"
Item "Check TeX|h" "buffer-chktex"
Item "Remove All Error Boxes|E" "error-remove-all"
Item "Open/Close float|l" "inset-toggle"
+ Submenu "Change tracking|h" "edit_change"
Separator
Item "Preferences...|P" "dialog-preferences"
Item "Reconfigure|R" "reconfigure"
Item "ASCII as Paragraphs...|P" "file-insert-ascii-para"
End
+ Menu "edit_change"
+ Item "Track changes|T" "track-changes"
+ Item "Merge changes ...|M" "merge-changes"
+ Item "Accept all changes|A" "accept-all-changes"
+ Item "Reject all changes|R" "reject-all-changes"
+ End
#
# LAYOUT MENU
#
-src/BufferView.C
-src/BufferView_pimpl.C
-src/Chktex.C
-src/CutAndPaste.C
-src/LColor.C
-src/LaTeX.C
-src/LyXAction.C
-src/MenuBackend.C
src/buffer.C
src/bufferlist.C
+src/BufferView.C
src/bufferview_funcs.C
+src/BufferView_pimpl.C
+src/Chktex.C
src/converter.C
+src/CutAndPaste.bak.C
+src/CutAndPaste.C
src/debug.C
src/exporter.C
-src/frontends/LyXView.C
+src/frontends/controllers/biblio.C
src/frontends/controllers/ButtonController.h
+src/frontends/controllers/character.C
src/frontends/controllers/ControlAboutlyx.C
src/frontends/controllers/ControlBibtex.C
src/frontends/controllers/ControlCharacter.C
src/frontends/controllers/ControlSpellchecker.C
src/frontends/controllers/ControlThesaurus.C
src/frontends/controllers/ControlVCLog.C
-src/frontends/controllers/biblio.C
-src/frontends/controllers/character.C
src/frontends/controllers/frnt_lang.C
src/frontends/controllers/helper_funcs.C
src/frontends/gnome/GLog.C
+src/frontends/LyXView.C
src/frontends/qt2/Alert_pimpl.C
src/frontends/qt2/FileDialog.C
+src/frontends/qt2/lengthcombo.C
src/frontends/qt2/QAbout.C
src/frontends/qt2/QBibitem.C
src/frontends/qt2/QBibtex.C
src/frontends/qt2/QDelimiterDialog.C
src/frontends/qt2/QDocument.C
src/frontends/qt2/QDocumentDialog.C
-src/frontends/qt2/QERT.C
src/frontends/qt2/QError.C
+src/frontends/qt2/QERT.C
src/frontends/qt2/QExternal.C
src/frontends/qt2/QExternalDialog.C
src/frontends/qt2/QFloat.C
src/frontends/qt2/QGraphicsDialog.C
src/frontends/qt2/QInclude.C
src/frontends/qt2/QIndex.C
-src/frontends/qt2/QLPrintDialog.C
src/frontends/qt2/QLog.C
+src/frontends/qt2/QLPrintDialog.C
src/frontends/qt2/QMathDialog.C
src/frontends/qt2/QMathMatrixDialog.C
src/frontends/qt2/QMinipage.C
src/frontends/qt2/QTexinfo.C
src/frontends/qt2/QThesaurus.C
src/frontends/qt2/QToc.C
+src/frontends/qt2/QtView.C
src/frontends/qt2/QURL.C
src/frontends/qt2/QVCLog.C
src/frontends/qt2/QWrap.C
-src/frontends/qt2/QtView.C
-src/frontends/qt2/lengthcombo.C
src/frontends/xforms/Alert_pimpl.C
src/frontends/xforms/ColorHandler.C
+src/frontends/xforms/combox.C
src/frontends/xforms/FileDialog.C
src/frontends/xforms/FormAboutlyx.C
src/frontends/xforms/FormBase.C
src/frontends/xforms/FormBibitem.C
src/frontends/xforms/FormBibtex.C
+src/frontends/xforms/FormChanges.C
src/frontends/xforms/FormCharacter.C
src/frontends/xforms/FormCitation.C
src/frontends/xforms/FormDocument.C
-src/frontends/xforms/FormERT.C
src/frontends/xforms/FormError.C
+src/frontends/xforms/FormERT.C
src/frontends/xforms/FormExternal.C
src/frontends/xforms/FormFiledialog.C
src/frontends/xforms/FormFloat.C
src/frontends/xforms/FormUrl.C
src/frontends/xforms/FormVCLog.C
src/frontends/xforms/FormWrap.C
-src/frontends/xforms/Menubar_pimpl.C
-src/frontends/xforms/XMiniBuffer.C
-src/frontends/xforms/combox.C
src/frontends/xforms/input_validators.C
+src/frontends/xforms/Menubar_pimpl.C
src/frontends/xforms/xforms_helpers.C
+src/frontends/xforms/XMiniBuffer.C
src/gettext.h
src/importer.C
-src/insets/inset.C
src/insets/insetbib.C
+src/insets/inset.C
src/insets/insetcaption.C
src/insets/inseterror.C
src/insets/insetert.C
src/insets/insetwrap.C
src/kbsequence.C
src/language.C
+src/LaTeX.C
+src/LColor.C
src/lengthcommon.C
+src/LyXAction.C
src/lyx_cb.C
-src/lyx_main.C
src/lyxfind.C
src/lyxfont.C
src/lyxfunc.C
+src/lyx_main.C
src/lyxrc.C
src/lyxtextclasslist.C
src/lyxvc.C
src/mathed/math_hullinset.C
src/mathed/math_parboxinset.C
src/mathed/ref_inset.C
+src/MenuBackend.C
src/paragraph.C
src/support/filetools.C
src/tabular.C
-src/text.C
src/text2.C
src/text3.C
+src/text.C
+src/ext_l10n.h
#include "lyxlex.h"
#include "lyxtext.h"
#include "undo_funcs.h"
+#include "changes.h"
#include "frontends/Alert.h"
#include "frontends/Dialogs.h"
}
+Change const BufferView::getCurrentChange()
+{
+ return pimpl_->getCurrentChange();
+}
+
+
void BufferView::beforeChange(LyXText * text)
{
pimpl_->beforeChange(text);
toggleSelection(false);
tt->replaceSelectionWithString(this, replacestring);
- tt->setSelectionOverString(this, replacestring);
+ tt->setSelectionRange(this, replacestring.length());
// Go back so that replacement string is also spellchecked
for (string::size_type i = 0; i < replacestring.length() + 1; ++i) {
#include <boost/utility.hpp>
+class Change;
class LyXView;
class LyXText;
class TeXErrors;
void restorePosition(unsigned int i);
/// does the given bookmark have a saved position ?
bool isSavedPosition(unsigned int i);
+
+ /// return the current change at the cursor
+ Change const getCurrentChange();
/**
* This holds the mapping between buffer paragraphs and screen rows.
#include "ParagraphParameters.h"
#include "undo_funcs.h"
#include "funcrequest.h"
+#include "iterators.h"
+#include "lyxfind.h"
#include "insets/insetbib.h"
#include "insets/insettext.h"
}
+Change const BufferView::Pimpl::getCurrentChange()
+{
+ if (!bv_->buffer()->params.tracking_changes)
+ return Change(Change::UNCHANGED);
+
+ LyXText * t(bv_->getLyXText());
+
+ if (!t->selection.set())
+ return Change(Change::UNCHANGED);
+
+ LyXCursor const & cur(t->selection.start);
+ return cur.par()->lookupChangeFull(cur.pos());
+}
+
+
void BufferView::Pimpl::beforeChange(LyXText * text)
{
toggleSelection();
}
+void BufferView::Pimpl::trackChanges()
+{
+ Buffer * buf(bv_->buffer());
+ bool const tracking(buf->params.tracking_changes);
+
+ if (!tracking) {
+ ParIterator const end = buf->par_iterator_end();
+ for (ParIterator it = buf->par_iterator_begin(); it != end; ++it) {
+ (*it)->trackChanges();
+ }
+ buf->params.tracking_changes = true;
+
+ // we cannot allow undos beyond the freeze point
+ buf->undostack.clear();
+ } else {
+ bv_->update(bv_->text, BufferView::SELECT | BufferView::FITCUR);
+ bv_->text->setCursor(bv_, &(*buf->paragraphs.begin()), 0);
+#warning changes FIXME
+ //moveCursorUpdate(false);
+
+ bool found = lyxfind::findNextChange(bv_);
+ if (found) {
+ owner_->getDialogs().showMergeChanges();
+ return;
+ }
+
+ ParIterator const end = buf->par_iterator_end();
+ for (ParIterator it = buf->par_iterator_begin(); it != end; ++it) {
+ (*it)->untrackChanges();
+ }
+ buf->params.tracking_changes = false;
+ }
+
+ buf->redostack.clear();
+}
+
+
bool BufferView::Pimpl::dispatch(FuncRequest const & ev)
{
lyxerr[Debug::ACTION] << "BufferView::Pimpl::Dispatch:"
}
break;
+ case LFUN_TRACK_CHANGES:
+ trackChanges();
+ break;
+
+ case LFUN_MERGE_CHANGES:
+ owner_->getDialogs().showMergeChanges();
+ break;
+
+ case LFUN_ACCEPT_ALL_CHANGES: {
+ bv_->update(bv_->text, BufferView::SELECT | BufferView::FITCUR);
+ bv_->text->setCursor(bv_, &(*bv_->buffer()->paragraphs.begin()), 0);
+#warning FIXME changes
+ //moveCursorUpdate(false);
+
+ while (lyxfind::findNextChange(bv_)) {
+ bv_->getLyXText()->acceptChange(bv_);
+ }
+ update(bv_->text,
+ BufferView::SELECT | BufferView::FITCUR | BufferView::CHANGE);
+ break;
+ }
+
+ case LFUN_REJECT_ALL_CHANGES: {
+ bv_->update(bv_->text, BufferView::SELECT | BufferView::FITCUR);
+ bv_->text->setCursor(bv_, &(*bv_->buffer()->paragraphs.begin()), 0);
+#warning FIXME changes
+ //moveCursorUpdate(false);
+
+ while (lyxfind::findNextChange(bv_)) {
+ bv_->getLyXText()->rejectChange(bv_);
+ }
+ update(bv_->text,
+ BufferView::SELECT | BufferView::FITCUR | BufferView::CHANGE);
+ break;
+ }
+
+ case LFUN_ACCEPT_CHANGE: {
+ bv_->getLyXText()->acceptChange(bv_);
+ update(bv_->text,
+ BufferView::SELECT | BufferView::FITCUR | BufferView::CHANGE);
+ break;
+ }
+
+ case LFUN_REJECT_CHANGE: {
+ bv_->getLyXText()->rejectChange(bv_);
+ update(bv_->text,
+ BufferView::SELECT | BufferView::FITCUR | BufferView::CHANGE);
+ break;
+ }
+
case LFUN_UNKNOWN_ACTION:
ev.errorMessage(N_("Unknown function!"));
break;
#pragma interface
#endif
+class Change;
class LyXView;
class WorkArea;
class LyXScreen;
void cursorToggle();
///
bool available() const;
+ /// get the change at the cursor position
+ Change const getCurrentChange();
///
void beforeChange(LyXText *);
///
///
bool dispatch(FuncRequest const & ev);
private:
+ /// track changes for the document
+ void trackChanges();
+
///
friend class BufferView;
+2003-02-08 John Levon <levon@movementarian.org>
+
+ * lyxfind.C:
+ * lyxtext.h:
+ * text2.C:
+ * BufferView.C: change setSelectionOverString() to setSelectionRange()
+ and pass the size in explicitly
+
+ * BufferView_pimpl.h:
+ * BufferView_pimpl.C:
+ * BufferView.h:
+ * BufferView.C: add getCurrentChange()
+
+ * BufferView_pimpl.h:
+ * BufferView_pimpl.C: handle change lfuns
+
+ * CutAndPaste.C: merge the cut and copy code. Rework the cut code
+ for changes. Mark pasted paragraphs as new.
+
+ * support/lyxtime.h:
+ * support/lyxtime.C:
+ * DepTable.C: abstract time_t as lyx::time_type
+
+ * LColor.h:
+ * LColor.C: add colours for new text, deleted text, changebars
+
+ * LaTeXFeatures.C: add dvipost as a simple feature. Make the color
+ package use "usenames" option.
+
+ * commandtags.h:
+ * lyxfunc.C:
+ * LyXAction.C: add change lfuns
+
+ * Makefile.am:
+ * author.h:
+ * author.C: author handling
+
+ * buffer.h:
+ * buffer.C: add a per-buffer author list, with first entry as
+ current author. Handle new .lyx tokens for change tracking. Output
+ author list to .lyx file. Output dvipost stuff to .tex preamble.
+ Bump lyx format to 222.
+
+ * bufferlist.h:
+ * bufferlist.C: add setCurrentAuthor() to reset current author details
+ in all buffers.
+
+ * bufferparams.h:
+ * bufferparams.C: add param for tracking
+
+ * bufferview_funcs.C: output change info in minibuffer
+
+ * Makefile.am:
+ * changes.h:
+ * changes.C: add change-tracking structure
+
+ * debug.h:
+ * debug.C: add CHANGES debug flag
+
+ * lyxfind.h:
+ * lyxfind.C: add code for finding the next change piece
+
+ * lyxrc.h:
+ * lyxrc.C: add user_name and user_email
+
+ * lyxrow.h:
+ * lyxrow.C: add a metric for the top of the text line
+
+ * lyxtext.h:
+ * text.C: implement accept/rejectChange()
+
+ * lyxtext.h:
+ * text.C: paint changebars. Paint new/deleted text in the chosen colours.
+ Strike through deleted text.
+
+ * paragraph.h:
+ * paragraph.C:
+ * paragraph_pimpl.h:
+ * paragraph_pimpl.C: output change markers in .lyx and .tex. Pass in the current change
+ to the insert functions. Rework erase to mark text as deleted, adding
+ an eraseIntern() and a range-based erase(). Implement
+ per-paragraph change lookup and accept/reject.
+
+ * paragraph_funcs.C: Fixup paste for change tracking.
+
+ * tabular.C: mark added row/columns as new.
+
+ * text.C: fix rowLast() to never return -1. Don't allow spellchecking of deleted
+ text. Track transpose changes. Don't allow paragraph break or merge where appropriate.
+
+ * text2.C: leave cursor at end of selection after a cut.
+
2003-02-07 Jean-Marc Lasgouttes <Jean-Marc.Lasgouttes@inria.fr>
* text.C (getLengthMarkerHeight):
#endif
#include "CutAndPaste.h"
-//#include "debug.h"
#include "BufferView.h"
#include "buffer.h"
#include "paragraph.h"
#include "lyxtextclasslist.h"
#include "undo_funcs.h"
#include "paragraph_funcs.h"
+#include "debug.h"
#include "insets/inseterror.h"
#include "BoostFormat.h"
+using std::endl;
using std::pair;
using lyx::pos_type;
using lyx::textclass_type;
namespace {
+// FIXME: stupid name
Paragraph * buf = 0;
textclass_type textclass = 0;
if (!startpar || (start > startpar->size()))
return false;
- if (realcut)
- DeleteBuffer();
-
- textclass = tc;
+ if (realcut) {
+ copySelection(startpar, *endpar, start, end, tc);
+ }
- if (!(*endpar) || startpar == (*endpar)) {
- // only within one paragraph
- if (realcut) {
- buf = new Paragraph;
- buf->layout(startpar->layout());
+ if (!endpar || startpar == *endpar) {
+ if (startpar->erase(start, end)) {
+ // Some chars were erased, go to start to be safe
+ end = start;
}
- pos_type i = start;
- if (end > startpar->size())
- end = startpar->size();
- for (; i < end; ++i) {
- if (realcut)
- startpar->copyIntoMinibuffer(*current_view->buffer(),
- start);
- startpar->erase(start);
- if (realcut)
- buf->insertFromMinibuffer(buf->size());
- }
- end = start - 1;
- } else {
- // more than one paragraph
- breakParagraphConservative(current_view->buffer()->params,
- *endpar,
- end);
- *endpar = (*endpar)->next();
+ return true;
+ }
+
+ bool actually_erased = false;
+
+ // clear end/begin fragments of the first/last par in selection
+ actually_erased |= (startpar)->erase(start, startpar->size());
+ if ((*endpar)->erase(0, end)) {
+ actually_erased = true;
end = 0;
-
- breakParagraphConservative(current_view->buffer()->params,
- startpar,
- start);
-
- // store the selection
- if (realcut) {
- buf = startpar->next();
- buf->previous(0);
- } else {
- startpar->next()->previous(0);
+ }
+
+ // Loop through the deleted pars if any, erasing as needed
+
+ Paragraph * pit = startpar->next();
+
+ while (1) {
+ // *endpar can be 0
+ if (!pit)
+ break;
+
+ Paragraph * next = pit->next();
+
+ // "erase" the contents of the par
+ if (pit != *endpar) {
+ actually_erased |= pit->erase(0, pit->size());
+
+ // remove the par if it's now empty
+ if (actually_erased) {
+ pit->previous()->next(pit->next());
+ if (next) {
+ next->previous(pit->previous());
+ }
+ lyxerr << "deleting pit " << pit << endl;
+
+ delete pit;
+ }
}
- (*endpar)->previous()->next(0);
+
+ if (pit == *endpar)
+ break;
+
+ pit = next;
+ }
- // cut the selection
- startpar->next(*endpar);
+#if 0 // FIXME: why for cut but not copy ?
+ // the cut selection should begin with standard layout
+ if (realcut) {
+ buf->params().clear();
+ buf->bibkey = 0;
+ buf->layout(textclasslist[buffer->params.textclass].defaultLayoutName());
+ }
+#endif
- (*endpar)->previous(startpar);
+ if (!startpar->next())
+ return true;
+
+ Buffer * buffer = current_view->buffer();
- // the cut selection should begin with standard layout
- if (realcut) {
- buf->params().clear();
- buf->bibkey = 0;
- buf->layout(current_view->buffer()->params.getLyXTextClass().defaultLayout());
- }
+ if (doclear) {
+ startpar->next()->stripLeadingSpaces();
+ }
- // paste the paragraphs again, if possible
- if (doclear)
- startpar->next()->stripLeadingSpaces();
- if (startpar->hasSameLayout(startpar->next()) ||
- startpar->next()->empty()) {
- mergeParagraph(current_view->buffer()->params, startpar);
- (*endpar) = startpar; // this because endpar gets deleted here!
- }
- // this paragraph's are of noone's owner!
- Paragraph * p = buf;
- while (p) {
- p->setInsetOwner(0);
- p = p->next();
- }
+ if (!actually_erased)
+ return true;
+
+ // paste the paragraphs again, if possible
+ if (startpar->hasSameLayout(startpar->next()) ||
+ startpar->next()->empty()) {
+ mergeParagraph(buffer->params, startpar);
+ // this because endpar gets deleted here!
+ (*endpar) = startpar;
}
+
return true;
}
-
+
bool CutAndPaste::copySelection(Paragraph * startpar, Paragraph * endpar,
int start, int end, char tc)
{
Paragraph * tmppar = startpar;
buf = new Paragraph(*tmppar, false);
Paragraph * tmppar2 = buf;
+ tmppar2->cleanChanges();
while (tmppar != endpar
&& tmppar->next()) {
tmppar2->next(new Paragraph(*tmppar, false));
tmppar2->next()->previous(tmppar2);
tmppar2 = tmppar2->next();
+ // reset change info
+ tmppar2->cleanChanges();
}
tmppar2->next(0);
// if necessary
if (((*par)->size() > pos) || !(*par)->next()) {
breakParagraphConservative(
- current_view->buffer()->params,
- *par,
- pos);
+ current_view->buffer()->params, *par, pos);
paste_the_end = true;
}
// set the end for redoing later
if (lastbuffer->next() && paste_the_end) {
if (lastbuffer->next()->hasSameLayout(lastbuffer)) {
mergeParagraph(current_view->buffer()->params, lastbuffer);
- } else if (lastbuffer->next()->empty()) {
+ } else if (!lastbuffer->next()->size()) {
lastbuffer->next()->makeSameLayout(lastbuffer);
mergeParagraph(current_view->buffer()->params, lastbuffer);
- } else if (lastbuffer->empty()) {
+ } else if (!lastbuffer->size()) {
lastbuffer->makeSameLayout(lastbuffer->next());
mergeParagraph(current_view->buffer()->params, lastbuffer);
} else
///
class CutAndPaste {
public:
- ///
+ /// realcut == false is we actually want a delete
static
bool cutSelection(Paragraph * startpar, Paragraph ** endpar,
int start, int & end, char tc, bool doclear = false,
#include "support/lyxlib.h"
#include "support/filetools.h"
#include "support/lstrings.h"
+#include "support/lyxtime.h"
#include <sys/types.h>
#include <sys/stat.h>
void DepTable::update()
{
lyxerr[Debug::DEPEND] << "Updating DepTable..." << endl;
- time_t start_time = time(0);
+ lyx::time_type const start_time = lyx::current_time();
DepList::iterator itr = deplist.begin();
while (itr != deplist.end()) {
}
++itr;
}
- time_t time_sec = time(0) - start_time;
+ lyx::time_type const time_sec = lyx::current_time() - start_time;
lyxerr[Debug::DEPEND] << "Finished updating DepTable ("
<< time_sec << " sec)." << endl;
}
{ error, N_("LaTeX error"), "error", "Red", "error" },
{ eolmarker, N_("end-of-line marker"), "eolmarker", "Brown", "eolmarker" },
{ appendixline, N_("appendix line"), "appendixline", "Brown", "appendixline" },
+ { changebar, N_("change bar"), "changebar", "Blue", "changebar" },
+ { strikeout, N_("Deleted text"), "strikeout", "Red", "strikeout" },
+ { newtext, N_("Added text"), "newtext", "Blue", "newtext" },
{ added_space, N_("added space markers"), "added_space", "Brown", "added_space" },
{ topline, N_("top/bottom line"), "topline", "Brown", "topline" },
{ tabularline, N_("tabular line"), "tabularline", "black",
added_space,
/// Appendix line color
appendixline,
+ /// changebar color
+ changebar,
+ /// strike-out color
+ strikeout,
+ /// added text color
+ newtext,
/// Top and bottom line color
topline,
/// Table line color
"varioref",
"prettyref",
"float",
- "wasy"
+ "wasy",
+ "dvipost"
};
-const int nb_simplefeatures = sizeof(simplefeatures) / sizeof(char const *);
+int const nb_simplefeatures = sizeof(simplefeatures) / sizeof(char const *);
}
// color.sty
if (isRequired("color")) {
if (params.graphicsDriver == "default")
- packages << "\\usepackage{color}\n";
+ packages << "\\usepackage[usenames]{color}\n";
else
packages << "\\usepackage["
<< params.graphicsDriver
+ << ",usenames"
<< "]{color}\n";
}
{ LFUN_FORKS_KILL, "kill-forks",
N_("Kill the forked process with this PID"), NoBuffer },
{ LFUN_TOOLTIPS_TOGGLE, "toggle-tooltips", "", NoBuffer },
+ { LFUN_TRACK_CHANGES, "track-changes", N_("Begin tracking changes"), Noop },
+ { LFUN_MERGE_CHANGES, "merge-changes", N_("Merge changes"), Noop },
+ { LFUN_ACCEPT_CHANGE, "accept-change", N_("Accept selected change"), Noop },
+ { LFUN_REJECT_CHANGE, "reject-change", N_("Reject selected change"), Noop },
+ { LFUN_ACCEPT_ALL_CHANGES, "accept-all-changes", N_("Accept all changes"), Noop },
+ { LFUN_REJECT_ALL_CHANGES, "reject-all-changes", N_("Reject all changes"), Noop },
{ LFUN_NOACTION, "", "", Noop }
};
ToolbarDefaults.C \
ToolbarDefaults.h \
WordLangTuple.h \
+ author.C \
+ author.h \
boost.C \
boost-inst.C \
box.h \
bufferparams.h \
bufferview_funcs.C \
bufferview_funcs.h \
+ changes.C \
+ changes.h \
chset.C \
chset.h \
commandtags.h \
--- /dev/null
+/**
+ * \file author.C
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
+ *
+ * \author John Levon
+ *
+ * Full author contact details are available in file CREDITS
+ */
+
+#include <config.h>
+
+#include "author.h"
+
+#include "debug.h"
+
+#include "support/LAssert.h"
+#include "support/LOstream.h"
+#include "support/LIstream.h"
+#include "support/lstrings.h"
+
+using std::endl;
+
+namespace {
+ int cur_id;
+}
+
+
+bool operator==(Author const & l, Author const & r)
+{
+ return l.name() == r.name() && l.email() == r.email();
+}
+
+
+std::ostream & operator<<(std::ostream & os, Author const & a)
+{
+ os << "\"" << a.name() << "\" " << a.email();
+ return os;
+}
+
+std::istream & operator>>(std::istream & is, Author & a)
+{
+ string s;
+ getline(is, s);
+ a.name_ = trim(token(s, '\"', 1));
+ a.email_ = trim(token(s, '\"', 2));
+ lyxerr << "Read name " << a.name_ << " email " << a.email_ << endl;
+ return is;
+}
+
+
+int AuthorList::record(Author const & a)
+{
+ Authors::const_iterator it(authors_.begin());
+ Authors::const_iterator itend(authors_.end());
+
+ for (; it != itend; ++it) {
+ if (it->second == a)
+ return it->first;
+ }
+
+ lyxerr[Debug::CHANGES] << "Adding author " << a << endl;
+
+ authors_[cur_id++] = a;
+ return cur_id - 1;
+}
+
+
+void AuthorList::record(int id, Author const & a)
+{
+ lyx::Assert(id < authors_.size());
+
+ authors_[id] = a;
+}
+
+
+Author const & AuthorList::get(int id)
+{
+ Authors::const_iterator it(authors_.find(id));
+ lyx::Assert(it != authors_.end());
+ return it->second;
+}
+
+
+AuthorList::Authors::const_iterator AuthorList::begin() const
+{
+ return authors_.begin();
+}
+
+
+AuthorList::Authors::const_iterator AuthorList::end() const
+{
+ return authors_.end();
+}
--- /dev/null
+/**
+ * \file author.h
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
+ *
+ * \author John Levon
+ *
+ * Full author contact details are available in file CREDITS
+ */
+
+#ifndef AUTHOR_H
+#define AUTHOR_H
+
+#include <map>
+#include <iosfwd>
+
+#include "LString.h"
+
+class Author {
+public:
+ Author() {}
+
+ Author(string n, string e)
+ : name_(n), email_(e) {}
+
+ string const name() const {
+ return name_;
+ }
+
+ string const email() const {
+ return email_;
+ }
+
+ friend std::istream & operator>>(std::istream & os, Author & a);
+
+private:
+ string name_;
+
+ string email_;
+};
+
+
+class AuthorList {
+public:
+ int record(Author const & a);
+
+ void record(int id, Author const & a);
+
+ Author const & get(int id);
+
+ typedef std::map<int, Author> Authors;
+
+ Authors::const_iterator begin() const;
+
+ Authors::const_iterator end() const;
+
+private:
+ Authors authors_;
+};
+
+bool operator==(Author const & l, Author const & r);
+
+std::ostream & operator<<(std::ostream & os, Author const & a);
+
+std::istream & operator>>(std::istream & os, Author & a);
+
+#endif // AUTHOR_H
#include "lyxtextclasslist.h"
#include "sgml.h"
#include "paragraph_funcs.h"
+#include "author.h"
#include "frontends/LyXView.h"
#include "support/FileInfo.h"
#include "support/lyxmanip.h"
#include "support/lyxalgo.h" // for lyx::count
+#include "support/lyxtime.h"
#include <boost/bind.hpp>
#include <boost/tuple/tuple.hpp>
namespace {
-const int LYX_FORMAT = 221;
+const int LYX_FORMAT = 222;
} // namespace anon
} else {
tmppath.erase();
}
+
+ // set initial author
+ authorlist.record(Author(lyxrc.user_name, lyxrc.user_email));
}
}
+AuthorList & Buffer::authors()
+{
+ return authorlist;
+}
+
+
/// Update window titles of all users
// Should work on a list
void Buffer::updateTitles() const
#endif
int unknown_layouts;
int unknown_tokens;
+vector<int> author_ids;
} // anon
{
unknown_layouts = 0;
unknown_tokens = 0;
+ author_ids.clear();
int pos = 0;
Paragraph::depth_type depth = 0;
}
+namespace {
+ // This stuff is, in the traditional LyX terminology, Super UGLY
+ // but this code is too b0rken to admit of a better solution yet
+ Change current_change;
+};
+
+
bool
Buffer::parseSingleLyXformat2Token(LyXLex & lex, Paragraph *& par,
Paragraph *& first_par,
if (token[0] != '\\') {
for (string::const_iterator cit = token.begin();
cit != token.end(); ++cit) {
- par->insertChar(pos, (*cit), font);
+ par->insertChar(pos, (*cit), font, current_change);
++pos;
}
} else if (token == "\\layout") {
else {
par = new Paragraph(par);
par->layout(params.getLyXTextClass().defaultLayout());
+ if (params.tracking_changes)
+ par->trackChanges();
}
pos = 0;
par->layout(params.getLyXTextClass()[layoutname]);
lex.next();
string const next_token = lex.getString();
if (next_token == "\\-") {
- par->insertChar(pos, '-', font);
+ par->insertChar(pos, '-', font, current_change);
} else if (next_token == "~") {
- par->insertChar(pos, ' ', font);
+ par->insertChar(pos, ' ', font, current_change);
} else {
lex.printError("Token `$$Token' "
"is in free space "
} else {
Inset * inset = new InsetSpecialChar;
inset->read(this, lex);
- par->insertInset(pos, inset, font);
+ par->insertInset(pos, inset, font, current_change);
}
++pos;
} else if (token == "\\i") {
Inset * inset = new InsetLatexAccent;
inset->read(this, lex);
- par->insertInset(pos, inset, font);
+ par->insertInset(pos, inset, font, current_change);
++pos;
} else if (token == "\\backslash") {
- par->insertChar(pos, '\\', font);
+ par->insertChar(pos, '\\', font, current_change);
++pos;
} else if (token == "\\begin_deeper") {
++depth;
} else if (token == "\\use_numerical_citations") {
lex.nextToken();
params.use_numerical_citations = lex.getInteger();
+ } else if (token == "\\tracking_changes") {
+ lex.nextToken();
+ params.tracking_changes = lex.getInteger();
+ // mark the first paragraph
+ if (params.tracking_changes)
+ par->trackChanges();
+ } else if (token == "\\author") {
+ lex.nextToken();
+ istringstream ss(lex.getString());
+ Author a;
+ ss >> a;
+ int aid(authorlist.record(a));
+ lyxerr << "aid is " << aid << endl;
+ lyxerr << "listed aid is " << author_ids.size() << endl;
+ author_ids.push_back(authorlist.record(a));
} else if (token == "\\paperorientation") {
int tmpret = lex.findToken(string_orientation);
if (tmpret == -1)
par->params().labelWidthString(lex.getString());
// do not delete this token, it is still needed!
} else if (token == "\\newline") {
- par->insertChar(pos, Paragraph::META_NEWLINE, font);
+ par->insertChar(pos, Paragraph::META_NEWLINE, font, current_change);
++pos;
} else if (token == "\\LyXTable") {
Inset * inset = new InsetTabular(*this);
inset->read(this, lex);
- par->insertInset(pos, inset, font);
+ par->insertInset(pos, inset, font, current_change);
++pos;
} else if (token == "\\hfill") {
- par->insertChar(pos, Paragraph::META_HFILL, font);
+ par->insertChar(pos, Paragraph::META_HFILL, font, current_change);
++pos;
+ } else if (token == "\\change_unchanged") {
+ // Hack ! Needed for empty paragraphs :/
+ if (!pos)
+ par->cleanChanges();
+ current_change = Change(Change::UNCHANGED);
+ } else if (token == "\\change_inserted") {
+ lex.nextToken();
+ istringstream istr(lex.getString());
+ int aid;
+ lyx::time_type ct;
+ istr >> aid;
+ istr >> ct;
+ current_change = Change(Change::INSERTED, author_ids[aid], ct);
+ } else if (token == "\\change_deleted") {
+ lex.nextToken();
+ istringstream istr(lex.getString());
+ int aid;
+ lyx::time_type ct;
+ istr >> aid;
+ istr >> ct;
+ current_change = Change(Change::DELETED, author_ids[aid], ct);
} else if (token == "\\bibitem") { // ale970302
if (!par->bibkey) {
InsetCommandParams p("bibitem", "dummy");
} else if (*cit == '\t') {
if (!layout->free_spacing && !par->isFreeSpacing()) {
// tabs are like spaces here
- par->insertChar(pos, ' ', font);
+ par->insertChar(pos, ' ', font, current_change);
++pos;
space_inserted = true;
} else {
const pos_type nb = 8 - pos % 8;
for (pos_type a = 0; a < nb ; ++a) {
- par->insertChar(pos, ' ', font);
+ par->insertChar(pos, ' ', font, current_change);
++pos;
}
space_inserted = true;
}
if (inset) {
- par->insertInset(pos, inset, font);
+ par->insertInset(pos, inset, font, current_change);
++pos;
}
}
// now write out the buffer paramters.
params.writeFile(ofs);
+ // if we're tracking, list all possible authors
+ if (params.tracking_changes) {
+ AuthorList::Authors::const_iterator it = authorlist.begin();
+ AuthorList::Authors::const_iterator end = authorlist.end();
+ for (; it != end; ++it) {
+ ofs << "\\author " << it->second << "\n";
+ }
+ }
+
Paragraph::depth_type depth = 0;
// this will write out all the paragraphs
preamble += "\\makeatother\n";
+ // dvipost settings come after everything else
+ if (params.tracking_changes) {
+ preamble += "\\dvipostlayout\n";
+ preamble += "\\dvipost{osstart color push Red}\n";
+ preamble += "\\dvipost{osend color pop}\n";
+ preamble += "\\dvipost{cbstart color push Blue}\n";
+ preamble += "\\dvipost{cbend color pop} \n";
+ }
+
os << preamble;
if (only_preamble)
{
LyXTextClass const & tclass = params.getLyXTextClass();
+ if (params.tracking_changes) {
+ features.require("dvipost");
+ features.require("color");
+ }
+
// AMS Style is at document level
if (params.use_amsmath || tclass.provides(LyXTextClass::amsmath))
features.require("amsmath");
#include "texrow.h"
#include "ParagraphList.h"
#include "paragraph.h"
+#include "author.h"
#include <boost/shared_ptr.hpp>
/// Used when typesetting to place errorboxes.
TexRow texrow;
+
+ /// the author list for the document
+ AuthorList & authors();
+
private:
+ /// the author list
+ AuthorList authorlist;
+
/// is save needed
mutable bool lyx_clean;
return b;
}
+
+
+void BufferList::setCurrentAuthor(string const & name, string const & email)
+{
+ BufferStorage::iterator it = bstore.begin();
+ BufferStorage::iterator end = bstore.end();
+ for (; it != end; ++it) {
+ (*it)->authors().record(0, Author(name, email));
+ }
+}
Buffer * getBuffer(string const &);
/// returns a pointer to the buffer with the given number.
Buffer * getBuffer(unsigned int);
+
+ /// reset current author for all buffers
+ void setCurrentAuthor(string const & name, string const & email);
+
private:
/// ask to save a buffer on quit
bool qwriteOne(Buffer * buf, string const & fname,
use_amsmath = false;
use_natbib = false;
use_numerical_citations = false;
+ tracking_changes = false;
secnumdepth = 3;
tocdepth = 3;
language = default_language;
}
}
}
+
+ os << "\\tracking_changes " << tracking_changes << "\n";
}
bool use_natbib;
///
bool use_numerical_citations;
+ /// revision tracking for this buffer ?
+ bool tracking_changes;
/// Time ago we agreed that this was a buffer property [ale990407]
string parentname;
private:
#include "language.h"
#include "gettext.h"
#include "ParagraphParameters.h"
+#include "author.h"
+#include "changes.h"
#include "frontends/Alert.h"
ostringstream state;
if (!bv->available())
- return "";
+ return string();
- // I think we should only show changes from the default
- // font. (Asger)
LyXText * text = bv->getLyXText();
Buffer * buffer = bv->buffer();
+ LyXCursor const & c(text->cursor);
+
+ bool const show_change = buffer->params.tracking_changes
+ && c.pos() != c.par()->size()
+ && c.par()->lookupChange(c.pos()) != Change::UNCHANGED;
+
+ if (show_change) {
+ Change change(c.par()->lookupChangeFull(c.pos()));
+ Author const & a(bv->buffer()->authors().get(change.author));
+ state << _("Change: ") << a.name();
+ if (!a.email().empty()) {
+ state << " (" << a.email() << ")";
+ }
+ if (change.changetime)
+ state << _(" at ") << ctime(&change.changetime);
+ state << " : ";
+ }
+
+ // I think we should only show changes from the default
+ // font. (Asger)
LyXFont font = text->real_current_font;
LyXFont const & defaultfont =
buffer->params.getLyXTextClass().defaultfont();
--- /dev/null
+/**
+ * \file changes.C
+ * Copyright 2002 the LyX Team
+ * Read the file COPYING
+ *
+ * Record changes in a paragraph.
+ *
+ * \author John Levon <levon@movementarian.org>
+ */
+
+#include <config.h>
+
+#include "changes.h"
+#include "debug.h"
+#include "author.h"
+
+#include "support/LAssert.h"
+#include "support/LOstream.h"
+
+using std::vector;
+using std::endl;
+using lyx::pos_type;
+
+bool operator==(Change const & l, Change const & r)
+{
+ return l.type == r.type && l.author == r.author
+ && l.changetime == r.changetime;
+}
+
+
+bool operator!=(Change const & l, Change const & r)
+{
+ return !(l == r);
+}
+
+
+bool operator==(Changes::Range const & r1, Changes::Range const & r2)
+{
+ return r1.start == r2.start && r1.end == r2.end;
+}
+
+
+bool operator!=(Changes::Range const & r1, Changes::Range const & r2)
+{
+ return !(r1 == r2);
+}
+
+
+bool Changes::Range::contains(Range const & r) const
+{
+ return r.start >= start && r.end <= end;
+}
+
+
+bool Changes::Range::contained(Range const & r) const
+{
+ return r.contains(*this);
+}
+
+
+bool Changes::Range::contains(pos_type pos) const
+{
+ return pos >= start && pos < end;
+}
+
+
+bool Changes::Range::loose_contains(pos_type pos) const
+{
+ return pos >= start && pos <= end;
+}
+
+
+bool Changes::Range::intersects(Range const & r) const
+{
+ return contained(r) || contains(r)
+ || contains(r.start) || contains(r.end);
+}
+
+
+Changes::Changes(Change::Type type)
+ : empty_type_(type)
+{
+}
+
+
+Changes::~Changes()
+{
+}
+
+
+Changes::Changes(Changes const & c)
+{
+ table_ = c.table_;
+}
+
+
+void Changes::record(Change change, pos_type pos)
+{
+ lyxerr[Debug::CHANGES] << "record " << change.type
+ << " at pos " << pos << " with total "
+ << table_.size() << " changes." << endl;
+
+ switch (change.type) {
+ case Change::INSERTED:
+ add(change, pos);
+ break;
+ case Change::DELETED:
+ del(change, pos);
+ break;
+ case Change::UNCHANGED:
+ set(Change::UNCHANGED, pos);
+ break;
+ }
+}
+
+
+void Changes::set(Change change, pos_type pos)
+{
+ set(change, pos, pos + 1);
+}
+
+
+void Changes::set(Change::Type type, pos_type pos)
+{
+ set(type, pos, pos + 1);
+}
+
+
+void Changes::set(Change::Type type, pos_type start, pos_type end)
+{
+ set(Change(type), start, end);
+}
+
+
+void Changes::set(Change change, pos_type start, pos_type end)
+{
+ ChangeTable::iterator it = table_.begin();
+
+ lyxerr[Debug::CHANGES] << "changeset of " << change.type
+ << " author " << change.author << " time " << change.changetime
+ << " in range " << start << "," << end << endl;
+
+ Range const new_range(start, end);
+
+ // remove all sub-ranges
+ for (; it != table_.end();) {
+ if (new_range != it->range && it->range.contained(new_range)) {
+ lyxerr[Debug::CHANGES] << "Removing subrange "
+ << it->range.start << "," << it->range.end << endl;
+ it = table_.erase(it);
+ } else {
+ ++it;
+ }
+ }
+
+ it = table_.begin();
+ ChangeTable::iterator itend = table_.end();
+
+ // find a super-range
+ for (; it != itend; ++it) {
+ if (it->range.contains(new_range))
+ break;
+ }
+
+ if (it == itend) {
+ lyxerr[Debug::CHANGES] << "Inserting change at end" << endl;
+ table_.push_back(ChangeRange(start, end, change));
+ merge();
+ return;
+ }
+
+ if (change.type == it->change.type) {
+ lyxerr[Debug::CHANGES] << "Change set already." << endl;
+ it->change = change;
+ return;
+ }
+
+ ChangeRange c(*it);
+
+ lyxerr[Debug::CHANGES] << "Using change of type " << c.change.type
+ << " over " << c.range.start << "," << c.range.end << endl;
+
+ // split head
+ if (c.range.start < start) {
+ it = table_.insert(it, ChangeRange(c.range.start, start, c.change));
+ lyxerr[Debug::CHANGES] << "Splitting head of type " << c.change.type
+ << " over " << c.range.start << "," << start << endl;
+ ++it;
+ }
+
+ // reset this as new type
+ it->range.start = start;
+ it->range.end = end;
+ it->change = change;
+ lyxerr[Debug::CHANGES] << "Resetting to new change" << endl;
+
+ // split tail
+ if (c.range.end > end) {
+ ++it;
+ table_.insert(it, ChangeRange(end, c.range.end, c.change));
+ lyxerr[Debug::CHANGES] << "Splitting tail of type " << c.change.type
+ << " over " << end << "," << c.range.end << endl;
+ }
+
+ check();
+ merge();
+}
+
+
+void Changes::erase(pos_type pos)
+{
+ ChangeTable::iterator it = table_.begin();
+ ChangeTable::iterator end = table_.end();
+
+ bool found = false;
+
+ for (; it != end; ++it) {
+ Range & range(it->range);
+
+ lyxerr[Debug::CHANGES] << "era:Range of type " << it->change.type << " is "
+ << it->range.start << "," << it->range.end << endl;
+
+ if (range.contains(pos)) {
+ found = true;
+ --range.end;
+ continue;
+ }
+
+ if (found) {
+ --range.start;
+ --range.end;
+ }
+ }
+ check();
+ merge();
+}
+
+
+void Changes::del(Change change, ChangeTable::size_type pos)
+{
+ // this case happens when building from .lyx
+ if (table_.empty()) {
+ set(change, pos);
+ return;
+ }
+
+ ChangeTable::iterator it = table_.begin();
+
+ for (; it != table_.end(); ++it) {
+ Range & range(it->range);
+
+ if (range.contains(pos)) {
+ if (it->change.type != Change::INSERTED) {
+ set(change, pos);
+ } else {
+ erase(pos);
+ }
+ break;
+ } else if (range.loose_contains(pos) && it + 1 == table_.end()) {
+ // this case happens when building from .lyx
+ set(change, pos);
+ break;
+ }
+ }
+}
+
+
+void Changes::add(Change change, ChangeTable::size_type pos)
+{
+ ChangeTable::iterator it = table_.begin();
+ ChangeTable::iterator end = table_.end();
+
+ bool found = false;
+
+ for (; it != end; ++it) {
+ Range & range(it->range);
+
+ if (!found && range.loose_contains(pos)) {
+ found = true;
+ lyxerr[Debug::CHANGES] << "Found range of "
+ << range.start << "," << range.end << endl;
+ ++range.end;
+ continue;
+ }
+
+ if (found) {
+ ++range.start;
+ ++range.end;
+ }
+ }
+ set(change, pos);
+}
+
+
+Change const Changes::lookupFull(pos_type pos) const
+{
+ if (!table_.size()) {
+ lyxerr[Debug::CHANGES] << "Empty, type is " << empty_type_ << endl;
+ return Change(empty_type_);
+ }
+
+ ChangeTable::const_iterator it = table_.begin();
+ ChangeTable::const_iterator end = table_.end();
+
+ for (; it != end; ++it) {
+ if (it->range.contains(pos))
+ return it->change;
+ }
+
+ check();
+ lyx::Assert(0);
+ return Change(Change::UNCHANGED);
+}
+
+
+Change::Type Changes::lookup(pos_type pos) const
+{
+ if (!table_.size()) {
+ lyxerr[Debug::CHANGES] << "Empty, type is " << empty_type_ << endl;
+ return empty_type_;
+ }
+
+ ChangeTable::const_iterator it = table_.begin();
+ ChangeTable::const_iterator end = table_.end();
+
+ for (; it != end; ++it) {
+ if (it->range.contains(pos))
+ return it->change.type;
+ }
+
+ check();
+ lyx::Assert(0);
+ return Change::UNCHANGED;
+}
+
+
+bool Changes::isChange(pos_type start, pos_type end) const
+{
+ if (!table_.size()) {
+ lyxerr[Debug::CHANGES] << "Empty, type is " << empty_type_ << endl;
+ return empty_type_ != Change::UNCHANGED;
+ }
+
+ ChangeTable::const_iterator it = table_.begin();
+ ChangeTable::const_iterator itend = table_.end();
+
+ for (; it != itend; ++it) {
+ lyxerr[Debug::CHANGES] << "Looking for " << start << ","
+ << end << " in " << it->range.start << ","
+ << it->range.end << "of type " << it->change.type << endl;
+ if (it->range.intersects(Range(start, end))
+ && it->change.type != Change::UNCHANGED) {
+ lyxerr[Debug::CHANGES] << "Found intersection of "
+ << start << "," << end << " with "
+ << it->range.start << "," << it->range.end
+ << " of type " << it->change.type << endl;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+bool Changes::isChangeEdited(lyx::pos_type start, lyx::pos_type end) const
+{
+ if (!table_.size()) {
+ lyxerr[Debug::CHANGES] << "Empty, type is " << empty_type_ << endl;
+ return empty_type_ != Change::INSERTED;
+ }
+
+ ChangeTable::const_iterator it = table_.begin();
+ ChangeTable::const_iterator itend = table_.end();
+
+ for (; it != itend; ++it) {
+ if (it->range.intersects(Range(start, end ? end - 1 : 0))
+ && it->change.type != Change::INSERTED) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+void Changes::merge()
+{
+ lyxerr[Debug::CHANGES] << "Starting merge" << endl;
+ ChangeTable::iterator it = table_.begin();
+
+ while (it != table_.end()) {
+ lyxerr[Debug::CHANGES] << "Range of type " << it->change.type << " is "
+ << it->range.start << "," << it->range.end << endl;
+
+ if (it->range.start == it->range.end) {
+ lyxerr[Debug::CHANGES] << "Removing empty range for pos "
+ << it->range.start << endl;
+ table_.erase(it);
+ // start again
+ it = table_.begin();
+ continue;
+ }
+
+ if (it + 1 == table_.end())
+ break;
+
+ if (it->change == (it + 1)->change) {
+ lyxerr[Debug::CHANGES] << "Merging equal ranges "
+ << it->range.start << "," << it->range.end
+ << " and " << (it + 1)->range.start << ","
+ << (it + 1)->range.end << endl;
+ (it + 1)->range.start = it->range.start;
+ table_.erase(it);
+ // start again
+ it = table_.begin();
+ continue;
+ }
+
+ ++it;
+ }
+
+ lyxerr[Debug::CHANGES] << "Merge ended" << endl;
+ check();
+}
+
+
+void Changes::check() const
+{
+ ChangeTable::const_iterator it = table_.begin();
+ ChangeTable::const_iterator end = table_.end();
+
+ bool dont_assert(true);
+
+ lyxerr[Debug::CHANGES] << "Changelist:" << endl;
+ for (; it != end; ++it) {
+ lyxerr[Debug::CHANGES] << "Range of type " << it->change.type << " is "
+ << it->range.start << "," << it->range.end << " author "
+ << it->change.author << " time " << it->change.changetime << endl;
+ if (it + 1 == end)
+ break;
+
+ Range const & range(it->range);
+ Range const & next((it + 1)->range);
+ if (range.end != next.start)
+ dont_assert = false;
+ }
+ lyxerr[Debug::CHANGES] << "End" << endl;
+ lyx::Assert(dont_assert);
+}
+
+
+int Changes::latexMarkChange(std::ostream & os, Change::Type old, Change::Type change)
+{
+ if (old == change)
+ return 0;
+
+ string const start("\\changestart{}");
+ string const end("\\changeend{}");
+ string const son("\\overstrikeon{}");
+ string const soff("\\overstrikeoff{}");
+
+ int column = 0;
+
+ if (old == Change::DELETED) {
+ os << soff;
+ column += soff.length();
+ }
+
+ switch (change) {
+ case Change::UNCHANGED:
+ os << end;
+ column += end.length();
+ break;
+
+ case Change::DELETED:
+ if (old == Change::UNCHANGED) {
+ os << start;
+ column += start.length();
+ }
+ os << son;
+ column += son.length();
+ break;
+
+ case Change::INSERTED:
+ if (old == Change::UNCHANGED) {
+ os << start;
+ column += start.length();
+ }
+ break;
+ }
+
+ return column;
+}
+
+
+void Changes::lyxMarkChange(std::ostream & os, int & column, lyx::time_type curtime,
+ Change const & old, Change const & change)
+{
+ if (old == change)
+ return;
+
+ column = 0;
+
+ switch (change.type) {
+ case Change::UNCHANGED:
+ os << "\n\\change_unchanged\n";
+ break;
+
+ case Change::DELETED: {
+ lyx::time_type t(change.changetime);
+ if (!t)
+ t = curtime;
+ os << "\n\\change_deleted " << change.author
+ << " " << t << "\n";
+
+ break;
+ }
+
+ case Change::INSERTED:
+ lyx::time_type t(change.changetime);
+ if (!t)
+ t = curtime;
+ os << "\n\\change_inserted " << change.author
+ << " " << t << "\n";
+ break;
+ }
+}
--- /dev/null
+/**
+ * \file changes.h
+ * Copyright 2002 the LyX Team
+ * Read the file COPYING
+ *
+ * Record changes in a paragraph.
+ *
+ * \author John Levon <levon@movementarian.org>
+ */
+
+#ifndef CHANGES_H
+#define CHANGES_H
+
+#include "support/types.h"
+#include "support/lyxtime.h"
+
+#include <vector>
+#include <iosfwd>
+#include <ctime>
+
+struct Change {
+ /// the type of change
+ enum Type {
+ UNCHANGED, // no change
+ INSERTED, // new text
+ DELETED // deleted text
+ };
+
+ Change(Type t = UNCHANGED, int a = 0, lyx::time_type ct = 0)
+ : type(t), author(a), changetime(ct) {}
+
+ Type type;
+
+ int author;
+
+ lyx::time_type changetime;
+};
+
+bool operator==(Change const & l, Change const & r);
+bool operator!=(Change const & l, Change const & r);
+
+class Changes {
+public:
+
+ Changes(Change::Type type);
+
+ ~Changes();
+
+ Changes(Changes const &);
+
+ /// reset "default" change type (for empty pars)
+ void reset(Change::Type type) {
+ empty_type_ = type;
+ }
+
+ /// set the position to the given change
+ void set(Change change, lyx::pos_type pos);
+
+ /// set the position to the given change
+ void set(Change::Type, lyx::pos_type pos);
+
+ /// set the range to the given change
+ void set(Change::Type, lyx::pos_type start, lyx::pos_type end);
+
+ /// set the range to the given change
+ void set(Change, lyx::pos_type start, lyx::pos_type end);
+
+ /// mark the given change and adjust
+ void record(Change, lyx::pos_type pos);
+
+ /// return the change type at the given position
+ Change::Type lookup(lyx::pos_type pos) const;
+
+ /// return the change at the given position
+ Change const lookupFull(lyx::pos_type pos) const;
+
+ /// return true if there is a change in the given range
+ bool isChange(lyx::pos_type start, lyx::pos_type end) const;
+
+ /// return true if there is a deleted or unchanged range contained
+ bool isChangeEdited(lyx::pos_type start, lyx::pos_type end) const;
+
+ /// remove the given entry
+ void erase(lyx::pos_type pos);
+
+ /// output latex to mark a transition between two changetypes
+ /// returns length of text outputted
+ static int latexMarkChange(std::ostream & os, Change::Type old, Change::Type change);
+
+ /// output .lyx file format for transitions between changes
+ static void lyxMarkChange(std::ostream & os, int & column,
+ lyx::time_type curtime, Change const & old, Change const & change);
+
+private:
+ struct Range {
+ Range(lyx::pos_type s, lyx::pos_type e)
+ : start(s), end(e) {}
+
+ // does this range contain r ?
+ bool contains(Range const & r) const;
+
+ // does this range contain pos ?
+ bool contains(lyx::pos_type pos) const;
+
+ // does this range contain pos, or can it be appended ?
+ bool loose_contains(lyx::pos_type pos) const;
+
+ // is this range contained within r ?
+ bool contained(Range const & r) const;
+
+ // do the ranges intersect ?
+ bool intersects(Range const & r) const;
+
+ lyx::pos_type start;
+ lyx::pos_type end;
+ };
+
+ friend bool operator==(Range const & r1, Range const & r2);
+ friend bool operator!=(Range const & r1, Range const & r2);
+
+ struct ChangeRange {
+ ChangeRange(lyx::pos_type s, lyx::pos_type e, Change c)
+ : range(Range(s, e)), change(c) {}
+ Range range;
+ Change change;
+ };
+
+ typedef std::vector<ChangeRange> ChangeTable;
+
+ /// our table of changes
+ ChangeTable table_;
+
+ /// change type for an empty paragraph
+ Change::Type empty_type_;
+
+ /// handle a delete
+ void del(Change change, ChangeTable::size_type pos);
+
+ /// handle an add
+ void add(Change change, ChangeTable::size_type pos);
+
+ /// merge neighbouring ranges
+ void merge();
+
+ /// consistency check
+ void check() const;
+
+};
+
+#endif // CHANGES_H
LFUN_MOUSE_TRIPLE, // André 9 Aug 2002
LFUN_EDIT, // André 16 Aug 2002
LFUN_INSET_WRAP, // Dekel 7 Apr 2002
+ LFUN_TRACK_CHANGES, // Levon 20021001 (cool date !)
+ LFUN_MERGE_CHANGES, // Levon 20021016
+ LFUN_ACCEPT_CHANGE, // Levon 20021016
+ LFUN_REJECT_CHANGE, // Levon 20021016
+ LFUN_ACCEPT_ALL_CHANGES, // Levon 20021016
+ LFUN_REJECT_ALL_CHANGES, // Levon 20021016
LFUN_LASTACTION /* this marks the end of the table */
};
{ Debug::WORKAREA, "workarea", N_("Workarea events")},
{ Debug::INSETTEXT, "insettext", N_("Insettext/tabular messages")},
{ Debug::GRAPHICS, "graphics", N_("Graphics conversion and loading")},
+ { Debug::CHANGES, "changes", N_("Change tracking")},
{ Debug::ANY, "any", N_("All debugging messages")}
};
Debug::MATHED | Debug::FONT | Debug::TCLASS | Debug::LYXVC |
Debug::LYXSERVER | Debug::ROFF | Debug::ACTION | Debug::LYXLEX |
Debug::DEPEND | Debug::INSETS | Debug::FILES | Debug::WORKAREA |
- Debug::INSETTEXT | Debug::GRAPHICS);
+ Debug::INSETTEXT | Debug::GRAPHICS | Debug::CHANGES);
Debug::type Debug::value(string const & val)
///
INSETTEXT = (1 << 20),
///
- GRAPHICS = (1 << 21)
+ GRAPHICS = (1 << 21),
+ /// change tracking
+ CHANGES = (1 << 22)
};
///
static type const ANY;
+2003-02-08 John Levon <levon@movementarian.org>
+
+ * Dialogs.h: add showMergeChanges()
+
2003-01-11 Juergen Spitzmueller <j.spitzmueller@gmx.de>
* FileDialog.h: implement opendir (browse directory) [bug 824]
void showLogFile();
/// display the top-level maths panel
void showMathPanel();
+ /// show the merge changes dialog
+ void showMergeChanges();
///
void showMinipage(InsetMinipage *);
///
+2003-02-08 John Levon <levon@movementarian.org>
+
+ * Makefile.am:
+ * ControlChanges.h:
+ * ControlChanges.C: add merge changes dialog
+
+ * ControlPrefs.h:
+ * ControlPrefs.C: add setCurrentAuthor()
+
2003-01-31 Angus Leeming <leeming@lyx.org>
* ViewBase.h: add an isVisible() pure virtual method.
--- /dev/null
+/**
+ * \file ControlChanges.C
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
+ *
+ * \author John Levon
+ *
+ * Full author contact details are available in file CREDITS
+ */
+
+#include <config.h>
+
+#ifdef __GNUG__
+#pragma implementation
+#endif
+
+#include "ViewBase.h"
+#include "ButtonControllerBase.h"
+#include "ControlChanges.h"
+#include "frontends/Dialogs.h"
+#include "frontends/LyXView.h"
+#include "buffer.h"
+#include "lyxfind.h"
+#include "lyxfunc.h"
+#include "debug.h"
+#include "BufferView.h"
+#include "support/lstrings.h"
+#include "funcrequest.h"
+#include "author.h"
+
+ControlChanges::ControlChanges(LyXView & lv, Dialogs & d)
+ : ControlDialogBD(lv, d)
+{
+}
+
+
+void ControlChanges::find()
+{
+ lyxfind::findNextChange(bufferview());
+}
+
+
+string const ControlChanges::getChangeDate()
+{
+ Change c(bufferview()->getCurrentChange());
+ if (c.type == Change::UNCHANGED || !c.changetime)
+ return string();
+ return ctime(&c.changetime);
+}
+
+
+string const ControlChanges::getChangeAuthor()
+{
+ Change c(bufferview()->getCurrentChange());
+ if (c.type == Change::UNCHANGED)
+ return string();
+
+ Author const & a(bufferview()->buffer()->authors().get(c.author));
+
+ string author(a.name());
+
+ if (!a.email().empty()) {
+ author += " (";
+ author += a.email() + ")";
+ }
+
+ return author;
+}
+
+
+void ControlChanges::accept()
+{
+ lv_.dispatch(FuncRequest(LFUN_ACCEPT_CHANGE));
+ lyxfind::findNextChange(bufferview());
+}
+
+
+void ControlChanges::reject()
+{
+ lv_.dispatch(FuncRequest(LFUN_REJECT_CHANGE));
+ lyxfind::findNextChange(bufferview());
+}
--- /dev/null
+// -*- C++ -*-
+/**
+ * \file ControlChanges.h
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
+ *
+ * \author John Levon
+ *
+ * Full author contact details are available in file CREDITS
+ */
+
+#ifndef CONTROLCHANGES_H
+#define CONTROLCHANGES_H
+
+#ifdef __GNUG__
+#pragma interface
+#endif
+
+#include "ControlDialog_impl.h"
+#include "LString.h"
+
+/**
+ * A controller for the merge changes dialog.
+ */
+class ControlChanges : public ControlDialogBD {
+public:
+ ControlChanges(LyXView &, Dialogs &);
+
+ /// find the next merge chunk and highlight it
+ void find();
+
+ /// return date of change
+ string const getChangeDate();
+
+ /// return author of change
+ string const getChangeAuthor();
+
+ /// accept the current merge
+ void accept();
+
+ /// reject the current merge
+ void reject();
+
+private:
+ /// not needed.
+ virtual void apply() {}
+};
+
+#endif // CONTROLCHANGES_H
#include "ViewBase.h"
#include "frontends/LyXView.h"
+#include "bufferlist.h"
#include "helper_funcs.h"
#include "gettext.h"
#include "support/filetools.h"
extern string system_lyxdir;
extern string user_lyxdir;
+extern BufferList bufferlist;
using std::endl;
using std::pair;
{
formats = form;
}
+
+
+void ControlPrefs::setCurrentAuthor()
+{
+ bufferlist.setCurrentAuthor(rc_.user_name, rc_.user_email);
+}
/// set global formats
void setFormats(Formats const & form);
+ /// reset the details for the current author for all buffers
+ void setCurrentAuthor();
+
private:
/// get current lyxrc
virtual void setParams();
ControlButtons.h \
ControlCharacter.C \
ControlCharacter.h \
+ ControlChanges.C \
+ ControlChanges.h \
ControlCitation.C \
ControlCitation.h \
ControlCommand.C \
+2003-02-08 John Levon <levon@movementarian.org>
+
+ * Makefile.am:
+ * forms/Makefile.am:
+ * forms/form_changes.fd:
+ * Dialogs.C:
+ * Dialogs2.C:
+ * Dialogs_impl.h:
+ * FormChanges.h:
+ * FormChanges.C: add changes dialog
+
+ * FormPreferences.h:
+ * FormPreferences.C:
+ * forms/form_preferences.fd: add Identity prefs
+
2003-01-31 Michael Schmitt <michael.schmitt@teststep.org>
* FormDocument.C:
: aboutlyx(lv, d),
bibitem(lv, d),
bibtex(lv, d),
+ changes(lv, d),
character(lv, d),
citation(lv, d),
document(lv, d),
}
+void Dialogs::showMergeChanges()
+{
+ pimpl_->changes.controller().show();
+}
+
+
void Dialogs::showCharacter()
{
pimpl_->character.controller().show();
#include "FormBrowser.h"
#include "forms/form_browser.h"
+#include "ControlChanges.h"
+#include "FormChanges.h"
+#include "forms/form_changes.h"
+
#include "ControlCharacter.h"
#include "FormCharacter.h"
#include "forms/form_character.h"
typedef GUI<ControlBibtex, FormBibtex, NoRepeatedApplyReadOnlyPolicy, xformsBC>
BibtexDialog;
+typedef GUI<ControlChanges, FormChanges, NoRepeatedApplyReadOnlyPolicy, xformsBC>
+ChangesDialog;
+
typedef GUI<ControlCharacter, FormCharacter, OkApplyCancelReadOnlyPolicy, xformsBC>
CharacterDialog;
AboutlyxDialog aboutlyx;
BibitemDialog bibitem;
- BibtexDialog bibtex;
- CharacterDialog character;
+ BibtexDialog bibtex;
+ ChangesDialog changes;
+ CharacterDialog character;
CitationDialog citation;
DocumentDialog document;
ErrorDialog error;
--- /dev/null
+/**
+ * \file FormChanges.C
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
+ *
+ * \author John Levon
+ *
+ * Full author contact details are available in file CREDITS
+ */
+
+#include <config.h>
+
+#ifdef __GNUG__
+#pragma implementation
+#endif
+
+#include "xformsBC.h"
+#include "ControlChanges.h"
+#include "FormChanges.h"
+#include "forms/form_changes.h"
+
+#include FORMS_H_LOCATION
+
+typedef FormCB<ControlChanges, FormDB<FD_changes> > base_class;
+
+FormChanges::FormChanges()
+ : base_class(_("LyX: Merge changes"))
+{}
+
+
+void FormChanges::build()
+{
+ dialog_.reset(build_changes(this));
+
+ bc().setCancel(dialog_->button_close);
+ bc().addReadOnly(dialog_->button_accept);
+ bc().addReadOnly(dialog_->button_reject);
+}
+
+
+void FormChanges::update()
+{
+ fl_set_object_label(dialog_->author, "");
+ fl_set_object_label(dialog_->date, "");
+ // FIXME: enable/disable accept/reject
+}
+
+
+ButtonPolicy::SMInput FormChanges::input(FL_OBJECT * obj, long)
+{
+ if (obj == dialog_->button_accept) {
+ controller().accept();
+ return ButtonPolicy::SMI_VALID;
+ }
+
+ if (obj == dialog_->button_reject) {
+ controller().reject();
+ return ButtonPolicy::SMI_VALID;
+ }
+
+ if (obj != dialog_->button_next)
+ return ButtonPolicy::SMI_VALID;
+
+ controller().find();
+
+ string author(controller().getChangeAuthor());
+ string date(controller().getChangeDate());
+ if (!date.empty()) {
+ date = _("Changed at : ") + date;
+ }
+ if (!author.empty()) {
+ author = _("Change made by : ") + author;
+ }
+ fl_set_object_label(dialog_->author, author.c_str());
+ fl_set_object_label(dialog_->date, date.c_str());
+
+ // Yes, this is needed.
+ fl_redraw_form(form());
+
+ return ButtonPolicy::SMI_VALID;
+}
--- /dev/null
+// -*- C++ -*-
+/**
+ * \file FormChanges.h
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
+ *
+ * \author John Levon
+ *
+ * Full author contact details are available in file CREDITS
+ */
+
+#ifndef FORMCHANGES_H
+#define FORMCHANGES_H
+
+#ifdef __GNUG__
+#pragma interface
+#endif
+
+#include "FormBase.h"
+
+class ControlChanges;
+struct FD_changes;
+
+/**
+ * This class provides an XForms implementation of the Merge Changes Dialog.
+ */
+class FormChanges : public FormCB<ControlChanges, FormDB<FD_changes> > {
+public:
+ FormChanges();
+
+private:
+ /// not needed.
+ virtual void apply() {}
+ /// Build the dialog
+ virtual void build();
+ /// update the dialog
+ virtual void update();
+
+ /// Filter the inputs
+ virtual ButtonPolicy::SMInput input(FL_OBJECT *, long);
+};
+
+#endif // FORMCHANGES_H
#include "LColor.h"
#include "Lsstream.h"
#include "funcrequest.h"
+#include "author.h"
#include "support/lyxfunctional.h"
#include "support/lyxmanip.h"
: base_class(_("Preferences"), false),
colors_(*this), converters_(*this), inputs_misc_(*this),
formats_(*this), interface_(*this), language_(*this),
- lnf_misc_(*this), outputs_misc_(*this), paths_(*this),
- printer_(*this), screen_fonts_(*this), spelloptions_(*this)
+ lnf_misc_(*this), identity_(*this), outputs_misc_(*this),
+ paths_(*this), printer_(*this), screen_fonts_(*this),
+ spelloptions_(*this)
{
}
interface_.build();
language_.build();
lnf_misc_.build();
+ identity_.build();
outputs_misc_.build();
paths_.build();
printer_.build();
fl_addto_tabfolder(look_n_feel_tab_->tabfolder_inner,
_("Misc"),
lnf_misc_.dialog()->form);
+ fl_addto_tabfolder(look_n_feel_tab_->tabfolder_inner,
+ _("Identity"),
+ identity_.dialog()->form);
// then build converters
fl_addto_tabfolder(converters_tab_->tabfolder_inner,
interface_.apply(rc);
language_.apply(rc);
lnf_misc_.apply(rc);
+ identity_.apply(rc);
outputs_misc_.apply(rc);
paths_.apply(rc);
printer_.apply(rc);
interface_.update(rc);
language_.update(rc);
lnf_misc_.update(rc);
+ identity_.update(rc);
outputs_misc_.update(rc);
paths_.update(rc);
printer_.update(rc);
}
+FormPreferences::Identity::Identity(FormPreferences & p)
+ : parent_(p)
+{}
+
+
+FD_preferences_identity const * FormPreferences::Identity::dialog()
+{
+ return dialog_.get();
+}
+
+
+void FormPreferences::Identity::apply(LyXRC & rc) const
+{
+ rc.user_name = fl_get_input(dialog_->input_user_name);
+ rc.user_email = fl_get_input(dialog_->input_user_email);
+ parent_.controller().setCurrentAuthor();
+}
+
+
+void FormPreferences::Identity::build()
+{
+ dialog_.reset(build_preferences_identity(&parent_));
+ fl_set_input_return(dialog_->input_user_name, FL_RETURN_CHANGED);
+ fl_set_input_return(dialog_->input_user_email, FL_RETURN_CHANGED);
+}
+
+
+void FormPreferences::Identity::update(LyXRC const & rc)
+{
+ fl_set_input(dialog_->input_user_name, rc.user_name.c_str());
+ fl_set_input(dialog_->input_user_email, rc.user_email.c_str());
+}
+
+
FormPreferences::InputsMisc::InputsMisc(FormPreferences & p)
: parent_(p)
{}
struct FD_preferences_interface;
struct FD_preferences_language;
struct FD_preferences_lnf_misc;
+struct FD_preferences_identity;
struct FD_preferences_inner_tab;
struct FD_preferences_outputs_misc;
struct FD_preferences_paths;
///
friend class LnFmisc;
+ class Identity {
+ public:
+ ///
+ Identity(FormPreferences & p);
+ ///
+ FD_preferences_identity const * dialog();
+ ///
+ void apply(LyXRC & rc) const;
+ ///
+ void build();
+ ///
+ string const feedback(FL_OBJECT const * const) const;
+ ///
+ void update(LyXRC const & rc);
+
+ private:
+ ///
+ FormPreferences & parent_;
+ ///
+ boost::scoped_ptr<FD_preferences_identity> dialog_;
+ };
+ friend class Identity;
+
///
class OutputsMisc {
public:
///
LnFmisc lnf_misc_;
///
+ Identity identity_;
+ ///
OutputsMisc outputs_misc_;
///
Paths paths_;
FormBibtex.h \
FormBrowser.C \
FormBrowser.h \
+ FormChanges.C \
+ FormChanges.h \
FormCharacter.C \
FormCharacter.h \
FormCitation.C \
form_bibitem.fd \
form_bibtex.fd \
form_browser.fd \
+ form_changes.fd \
form_character.fd \
form_citation.fd \
form_document.fd \
--- /dev/null
+Magic: 13000
+
+Internal Form Definition File
+ (do not change)
+
+Number of forms: 1
+Unit of measure: FL_COORD_PIXEL
+
+=============== FORM ===============
+Name: form_changes
+Width: 400
+Height: 190
+Number of Objects: 8
+
+--------------------
+class: FL_BOX
+type: UP_BOX
+box: 0 0 400 190
+boxtype: FL_UP_BOX
+colors: FL_COL1 FL_COL1
+alignment: FL_ALIGN_CENTER
+style: FL_NORMAL_STYLE
+size: FL_DEFAULT_SIZE
+lcol: FL_BLACK
+label:
+shortcut:
+resize: FL_RESIZE_ALL
+gravity: FL_NoGravity FL_NoGravity
+name:
+callback:
+argument:
+
+--------------------
+class: FL_BUTTON
+type: NORMAL_BUTTON
+box: 270 110 120 30
+boxtype: FL_UP_BOX
+colors: FL_COL1 FL_COL1
+alignment: FL_ALIGN_CENTER
+style: FL_NORMAL_STYLE
+size: FL_NORMAL_SIZE
+lcol: FL_BLACK
+label: Reject change|#R
+shortcut:
+resize: FL_RESIZE_NONE
+gravity: FL_SouthEast FL_SouthEast
+name: button_reject
+callback: C_FormBaseInputCB
+argument: 0
+
+--------------------
+class: FL_BUTTON
+type: NORMAL_BUTTON
+box: 10 110 100 30
+boxtype: FL_UP_BOX
+colors: FL_COL1 FL_COL1
+alignment: FL_ALIGN_CENTER
+style: FL_NORMAL_STYLE
+size: FL_NORMAL_SIZE
+lcol: FL_BLACK
+label: Next change|#N
+shortcut:
+resize: FL_RESIZE_NONE
+gravity: FL_SouthEast FL_SouthEast
+name: button_next
+callback: C_FormBaseInputCB
+argument: 0
+
+--------------------
+class: FL_BUTTON
+type: NORMAL_BUTTON
+box: 130 110 120 30
+boxtype: FL_UP_BOX
+colors: FL_COL1 FL_COL1
+alignment: FL_ALIGN_CENTER
+style: FL_NORMAL_STYLE
+size: FL_NORMAL_SIZE
+lcol: FL_BLACK
+label: Accept change|#A
+shortcut:
+resize: FL_RESIZE_NONE
+gravity: FL_SouthEast FL_SouthEast
+name: button_accept
+callback: C_FormBaseInputCB
+argument: 0
+
+--------------------
+class: FL_BUTTON
+type: NORMAL_BUTTON
+box: 310 150 80 30
+boxtype: FL_UP_BOX
+colors: FL_COL1 FL_COL1
+alignment: FL_ALIGN_CENTER
+style: FL_NORMAL_STYLE
+size: FL_NORMAL_SIZE
+lcol: FL_BLACK
+label: Close|^[
+shortcut:
+resize: FL_RESIZE_NONE
+gravity: FL_SouthEast FL_SouthEast
+name: button_close
+callback: C_FormBaseCancelCB
+argument: 0
+
+--------------------
+class: FL_TEXT
+type: NORMAL_TEXT
+box: 10 10 150 20
+boxtype: FL_FLAT_BOX
+colors: FL_COL1 FL_MCOL
+alignment: FL_ALIGN_LEFT|FL_ALIGN_INSIDE
+style: FL_NORMAL_STYLE
+size: FL_NORMAL_SIZE
+lcol: FL_BLACK
+label: Accept change ?
+shortcut:
+resize: FL_RESIZE_ALL
+gravity: FL_NoGravity FL_NoGravity
+name:
+callback:
+argument:
+
+--------------------
+class: FL_TEXT
+type: NORMAL_TEXT
+box: 10 40 380 30
+boxtype: FL_NO_BOX
+colors: FL_COL1 FL_MCOL
+alignment: FL_ALIGN_LEFT|FL_ALIGN_INSIDE
+style: FL_NORMAL_STYLE
+size: FL_NORMAL_SIZE
+lcol: FL_BLACK
+label: text
+shortcut:
+resize: FL_RESIZE_ALL
+gravity: FL_NoGravity FL_NoGravity
+name: author
+callback:
+argument:
+
+--------------------
+class: FL_TEXT
+type: NORMAL_TEXT
+box: 10 70 380 30
+boxtype: FL_NO_BOX
+colors: FL_COL1 FL_MCOL
+alignment: FL_ALIGN_LEFT|FL_ALIGN_INSIDE
+style: FL_NORMAL_STYLE
+size: FL_NORMAL_SIZE
+lcol: FL_BLACK
+label: text
+shortcut:
+resize: FL_RESIZE_ALL
+gravity: FL_NoGravity FL_NoGravity
+name: date
+callback:
+argument:
+
+==============================
+--------------------
Internal Form Definition File
(do not change)
-Number of forms: 14
+Number of forms: 15
Unit of measure: FL_COORD_PIXEL
SnapGrid: 5
callback: C_FormBaseInputCB
argument: 0
+=============== FORM ===============
+Name: form_preferences_identity
+Width: 450
+Height: 350
+Number of Objects: 3
+
+--------------------
+class: FL_BOX
+type: FLAT_BOX
+box: 0 0 450 350
+boxtype: FL_FLAT_BOX
+colors: FL_COL1 FL_COL1
+alignment: FL_ALIGN_CENTER
+style: FL_NORMAL_STYLE
+size: FL_DEFAULT_SIZE
+lcol: FL_BLACK
+label:
+shortcut:
+resize: FL_RESIZE_ALL
+gravity: FL_NoGravity FL_NoGravity
+name:
+callback:
+argument:
+
+--------------------
+class: FL_INPUT
+type: NORMAL_INPUT
+box: 125 25 220 35
+boxtype: FL_DOWN_BOX
+colors: FL_COL1 FL_MCOL
+alignment: FL_ALIGN_LEFT
+style: FL_NORMAL_STYLE
+size: FL_DEFAULT_SIZE
+lcol: FL_BLACK
+label: Real name : |#R
+shortcut:
+resize: FL_RESIZE_ALL
+gravity: FL_NoGravity FL_NoGravity
+name: input_user_name
+callback: C_FormBaseInputCB
+argument: 0
+
+--------------------
+class: FL_INPUT
+type: NORMAL_INPUT
+box: 125 80 220 35
+boxtype: FL_DOWN_BOX
+colors: FL_COL1 FL_MCOL
+alignment: FL_ALIGN_LEFT
+style: FL_NORMAL_STYLE
+size: FL_DEFAULT_SIZE
+lcol: FL_BLACK
+label: Email address : |#E
+shortcut:
+resize: FL_RESIZE_ALL
+gravity: FL_NoGravity FL_NoGravity
+name: input_user_email
+callback: C_FormBaseInputCB
+argument: 0
+
=============== FORM ===============
Name: form_preferences_spelloptions
Width: 450
+2003-02-08 John Levon <levon@movementarian.org>
+
+ * inset.h:
+ * inset.C:
+ * insetcollapsable.h:
+ * insetcollapsable.C:
+ * insettabular.h:
+ * insettabular.C:
+ * insettext.h:
+ * insettext.C:
+ add nextChange(). Make allowSpellcheck() const. Add markErased().
+
+ * insetert.C: ignore deleted text
+
+ * insettabular.C: make sure to keep change tracking working
+ properly.
+
2003-01-20 Michael Schmitt <michael.schmitt@teststep.org>
* insetert.C:
}
+bool UpdatableInset::nextChange(BufferView * bv, lyx::pos_type &)
+{
+ // we have to unlock ourself in this function by default!
+ bv->unlockInset(const_cast<UpdatableInset *>(this));
+ return false;
+}
+
+
bool UpdatableInset::searchForward(BufferView * bv, string const &,
bool, bool)
{
#include "LString.h"
#include "LColor.h"
#include "frontends/mouse_state.h"
+#include "support/types.h"
class LyXFont;
class BufferView;
///
// needed for spellchecking text
///
- virtual bool allowSpellcheck() { return false; }
+ virtual bool allowSpellcheck() const { return false; }
// should this inset be handled like a normal charater
virtual bool isChar() const { return false; }
minipage somewhere, it will be the width of this minipage */
virtual int latexTextWidth(BufferView *) const;
+ /// mark the inset contents as erased (for change tracking)
+ virtual void markErased() {}
+
/** Adds a LaTeX snippet to the Preview Loader for transformation
* into a bitmap image. Does not start the laoding process.
*
///
// needed for spellchecking text
///
- virtual bool allowSpellcheck() { return false; }
+ virtual bool allowSpellcheck() const { return false; }
///
virtual WordLangTuple const
selectNextWordToSpellcheck(BufferView *, float & value) const;
///
- virtual void selectSelectedWord(BufferView *) { return; }
+ virtual void selectSelectedWord(BufferView *) {}
///
virtual void toggleSelection(BufferView *, bool /*kill_selection*/) {
return;
}
+
+ /// find the next change in the inset
+ virtual bool nextChange(BufferView * bv, lyx::pos_type & length);
+
///
// needed for search/replace functionality
///
}
+void InsetCollapsable::markErased()
+{
+ inset.markErased();
+}
+
+
+bool InsetCollapsable::nextChange(BufferView * bv, lyx::pos_type & length)
+{
+ bool found = inset.nextChange(bv, length);
+
+ if (first_after_edit && !found)
+ close(bv);
+ else if (!found)
+ first_after_edit = false;
+ return found;
+}
+
+
bool InsetCollapsable::searchForward(BufferView * bv, string const & str,
bool cs, bool mw)
{
///
void close(BufferView *) const;
///
- bool allowSpellcheck() { return inset.allowSpellcheck(); }
+ bool allowSpellcheck() const { return inset.allowSpellcheck(); }
///
WordLangTuple const
selectNextWordToSpellcheck(BufferView *, float &) const;
void toggleSelection(BufferView * bv, bool kill_selection) {
inset.toggleSelection(bv, kill_selection);
}
+
+ void markErased();
+
+ bool nextChange(BufferView * bv, lyx::pos_type & length);
+
///
bool searchForward(BufferView * bv, string const & str,
bool = true, bool = false);
while (par) {
pos_type siz = par->size();
for (pos_type i = 0; i < siz; ++i) {
+ // ignore all struck out text
+ if (isDeletedText(par, i))
+ continue;
+
Paragraph::value_type c = par->getChar(i);
switch (c) {
case Paragraph::META_NEWLINE:
///
void close(BufferView *) const;
///
- bool allowSpellcheck() { return false; }
+ bool allowSpellcheck() const { return false; }
WordLangTuple const
selectNextWordToSpellcheck(BufferView *, float &) const;
setUndo(bv, Undo::DELETE,
bv->text->cursor.par(),
bv->text->cursor.par()->next());
- cutSelection();
+ cutSelection(bv->buffer()->params);
updateLocal(bv, INIT, true);
break;
case LFUN_COPY:
*(tabular->GetCellInset(n2)) = *(paste_tabular->GetCellInset(n1));
tabular->GetCellInset(n2)->setOwner(this);
tabular->GetCellInset(n2)->deleteLyXText(bv);
+ tabular->GetCellInset(n2)->markNew();
}
}
return true;
}
-bool InsetTabular::cutSelection()
+bool InsetTabular::cutSelection(BufferParams const & bp)
{
if (!hasSelection())
return false;
}
for (int i = sel_row_start; i <= sel_row_end; ++i) {
for (int j = sel_col_start; j <= sel_col_end; ++j) {
- tabular->GetCellInset(tabular->GetCellNumber(i, j))->clear();
+ tabular->GetCellInset(tabular->GetCellNumber(i, j))->clear(bp.tracking_changes);
}
}
return true;
}
+void InsetTabular::markErased()
+{
+ int cell = 0;
+
+ while (!tabular->IsLastCell(cell)) {
+ ++cell;
+ InsetText * inset = tabular->GetCellInset(cell);
+ inset->markErased();
+ }
+}
+
+
+bool InsetTabular::nextChange(BufferView * bv, lyx::pos_type & length)
+{
+ if (the_locking_inset) {
+ if (the_locking_inset->nextChange(bv, length)) {
+ updateLocal(bv, CELL, false);
+ return true;
+ }
+ if (tabular->IsLastCell(actcell))
+ return false;
+ ++actcell;
+ }
+ InsetText * inset = tabular->GetCellInset(actcell);
+ if (inset->nextChange(bv, length)) {
+ updateLocal(bv, FULL, false);
+ return true;
+ }
+ while (!tabular->IsLastCell(actcell)) {
+ ++actcell;
+ inset = tabular->GetCellInset(actcell);
+ if (inset->nextChange(bv, length)) {
+ updateLocal(bv, FULL, false);
+ return true;
+ }
+ }
+ return false;
+}
+
+
bool InsetTabular::searchForward(BufferView * bv, string const & str,
bool cs, bool mw)
{
ocol = actcol;
row = actrow;
}
+
string::size_type op = 0;
int cells = loctab->GetNumberOfCells();
p = 0;
cols = ocol;
rows = loctab->rows();
int const columns = loctab->columns();
+
while ((cell < cells) && (p < len) && (row < rows) &&
(p = buf.find_first_of("\t\n", p)) != string::npos)
{
class Painter;
class BufferView;
class Buffer;
+class BufferParams;
class Paragraph;
class InsetTabular : public UpdatableInset {
///
LyXCursor const & cursor(BufferView *) const;
///
- bool allowSpellcheck() { return true; }
+ bool allowSpellcheck() const { return true; }
///
WordLangTuple const
selectNextWordToSpellcheck(BufferView *, float & value) const;
void selectSelectedWord(BufferView *);
///
void toggleSelection(BufferView *, bool kill_selection);
+
+ void markErased();
+
+ /// find next change
+ bool nextChange(BufferView *, lyx::pos_type & length);
///
bool searchForward(BufferView *, string const &,
bool = true, bool = false);
///
bool pasteSelection(BufferView *);
///
- bool cutSelection();
+ bool cutSelection(BufferParams const & bp);
///
bool isRightToLeft(BufferView *);
///
{
paragraphs.set(new Paragraph);
paragraphs.begin()->layout(bp.getLyXTextClass().defaultLayout());
+ if (bp.tracking_changes)
+ paragraphs.begin()->trackChanges();
init();
}
}
-void InsetText::clear()
+void InsetText::clear(bool just_mark_erased)
{
+ if (just_mark_erased) {
+ ParagraphList::iterator it = paragraphs.begin();
+ ParagraphList::iterator end = paragraphs.end();
+ for (; it != end; ++it) {
+ it->markErased();
+ }
+ need_update = FULL;
+ return;
+ }
+
// This is a gross hack...
LyXLayout_ptr old_layout = paragraphs.begin()->layout();
Paragraph::depth_type depth = 0;
LyXFont font(LyXFont::ALL_INHERIT);
- clear();
+ clear(false);
+ if (buf->params.tracking_changes)
+ paragraphs.begin()->trackChanges();
+
while (lex.isOK()) {
lex.nextToken();
token = lex.getString();
}
+void InsetText::markNew(bool track_changes)
+{
+ ParagraphList::iterator pit = paragraphs.begin();
+ ParagraphList::iterator pend = paragraphs.end();
+ for (; pit != pend; ++pit) {
+ if (track_changes) {
+ pit->trackChanges();
+ } else {
+ // no-op when not tracking
+ pit->cleanChanges();
+ }
+ }
+}
+
+
void InsetText::setText(string const & data, LyXFont const & font)
{
- clear();
+ clear(false);
for (unsigned int i = 0; i < data.length(); ++i)
paragraphs.begin()->insertChar(i, data[i], font);
reinitLyXText();
}
+bool InsetText::nextChange(BufferView * bv, lyx::pos_type & length)
+{
+ bool clear = false;
+ if (!lt) {
+ lt = getLyXText(bv);
+ clear = true;
+ }
+ if (the_locking_inset) {
+ if (the_locking_inset->nextChange(bv, length))
+ return true;
+ lt->cursorRight(bv, true);
+ }
+ lyxfind::SearchResult result =
+ lyxfind::findNextChange(bv, lt, length);
+
+ if (result == lyxfind::SR_FOUND) {
+ LyXCursor cur = lt->cursor;
+ bv->unlockInset(bv->theLockingInset());
+ if (bv->lockInset(this))
+ locked = true;
+ lt->cursor = cur;
+ lt->setSelectionRange(bv, length);
+ updateLocal(bv, SELECTION, false);
+ }
+ if (clear)
+ lt = 0;
+ return result != lyxfind::SR_NOT_FOUND;
+}
+
+
bool InsetText::searchForward(BufferView * bv, string const & str,
bool cs, bool mw)
{
if (bv->lockInset(this))
locked = true;
lt->cursor = cur;
- lt->setSelectionOverString(bv, str);
+ lt->setSelectionRange(bv, str.length());
updateLocal(bv, SELECTION, false);
}
if (clear)
if (bv->lockInset(this))
locked = true;
lt->cursor = cur;
- lt->setSelectionOverString(bv, str);
+ lt->setSelectionRange(bv, str.length());
updateLocal(bv, SELECTION, false);
}
if (clear)
Paragraph * buf;
Paragraph * tmpbuf = newpar;
Paragraph * lastbuffer = buf = new Paragraph(*tmpbuf, false);
+ if (bparams.tracking_changes)
+ buf->cleanChanges();
while (tmpbuf->next()) {
tmpbuf = tmpbuf->next();
lastbuffer->next(new Paragraph(*tmpbuf, false));
lastbuffer->next()->previous(lastbuffer);
lastbuffer = lastbuffer->next();
+ if (bparams.tracking_changes)
+ lastbuffer->cleanChanges();
}
lastbuffer = &*(paragraphs.begin());
while (lastbuffer->next())
Inset * clone(Buffer const &, bool same_id = false) const;
///
InsetText & operator=(InsetText const & it);
- ///
- void clear();
+ /// empty inset to empty par, or just mark as erased
+ void clear(bool just_mark_erased);
///
void read(Buffer const *, LyXLex &);
///
///
void paragraph(Paragraph *);
///
- bool allowSpellcheck() { return true; }
+ bool allowSpellcheck() const { return true; }
///
WordLangTuple const
selectNextWordToSpellcheck(BufferView *, float & value) const;
void selectSelectedWord(BufferView *);
///
void toggleSelection(BufferView *, bool kill_selection);
+
+ /// mark as erased for change tracking
+ void markErased() { clear(true); };
+ /**
+ * Mark as new. Used when pasting in tabular, and adding rows
+ * or columns. Note that pasting will ensure that tracking already
+ * happens, and this just resets the changes for the copied text,
+ * whereas for row/col add, we need to start tracking changes
+ * for the (empty) paragraph contained.
+ */
+ void markNew(bool track_changes = false);
+ /// find next change
+ bool nextChange(BufferView *, lyx::pos_type & length);
+
///
bool searchForward(BufferView *, string const &,
bool = true, bool = false);
bool checkInsertChar(LyXFont &);
///
void getDrawFont(LyXFont &) const;
- ///
- void appendParagraphs(BufferParams const & bparams, Paragraph *);
+ /// append text onto the existing text
+ void appendParagraphs(BufferParams const & bp, Paragraph *);
+
///
void addPreview(grfx::PreviewLoader &) const;
// If there is a preferences file we read that instead
// of the old lyxrc file.
if (!readRcFile("preferences"))
- readRcFile("lyxrc");
+ readRcFile("lyxrc");
readEncodingsFile("encodings");
readLanguagesFile("languages");
#include "debug.h"
#include "gettext.h"
#include "insets/insettext.h"
+#include "changes.h"
using lyx::pos_type;
+using std::endl;
namespace lyxfind {
bv->update(text, BufferView::SELECT|BufferView::FITCUR);
bv->toggleSelection(false);
text->replaceSelectionWithString(bv, replacestr);
- text->setSelectionOverString(bv, replacestr);
+ text->setSelectionRange(bv, replacestr.length());
bv->update(text, BufferView::SELECT|BufferView::FITCUR|BufferView::CHANGE);
++replace_count;
}
if (result == SR_FOUND) {
bv->unlockInset(bv->theLockingInset());
bv->update(text, BufferView::SELECT|BufferView::FITCUR);
- text->setSelectionOverString(bv, searchstr);
+ text->setSelectionRange(bv, searchstr.length());
bv->toggleSelection(false);
bv->update(text, BufferView::SELECT|BufferView::FITCUR);
} else if (result == SR_NOT_FOUND) {
}
}
+
+SearchResult nextChange(BufferView * bv, LyXText * text, pos_type & length)
+{
+ Paragraph * par = text->cursor.par();
+ pos_type pos = text->cursor.pos();
+ Paragraph * prev_par = par;
+ UpdatableInset * inset;
+
+ while (par) {
+ if ((!par->size() || pos != par->size())
+ && par->lookupChange(pos) != Change::UNCHANGED)
+ break;
+
+ if (par->isInset(pos) &&
+ (inset = (UpdatableInset *)par->getInset(pos)) &&
+ (inset->isTextInset())) {
+ if (inset->nextChange(bv, length))
+ return SR_FOUND_NOUPDATE;
+ }
+
+ ++pos;
+
+ if (pos >= par->size()) {
+ prev_par = par;
+ par = par->next();
+ pos = 0;
+ }
+ }
+
+ if (par) {
+ text->setCursor(bv, par, pos);
+ Change orig_change = par->lookupChangeFull(pos);
+ pos_type end = pos;
+
+ for (; end != par->size(); ++end) {
+ Change change = par->lookupChangeFull(end);
+ if (change != orig_change) {
+ // slight UI optimisation: for replacements, we get
+ // text like : _old_new. Consider that as one change.
+ if (!(orig_change.type == Change::DELETED &&
+ change.type == Change::INSERTED))
+ break;
+ }
+ }
+ length = end - pos;
+ return SR_FOUND;
+ } else {
+ // make sure we end up at the end of the text,
+ // not the start point of the last search
+ text->setCursor(bv, prev_par, prev_par->size());
+ return SR_NOT_FOUND;
+ }
+}
+
+
+SearchResult findNextChange(BufferView * bv, LyXText * text, pos_type & length)
+{
+ if (text->selection.set())
+ text->cursor = text->selection.end;
+
+ bv->toggleSelection();
+ text->clearSelection();
+
+ return nextChange(bv, text, length);
+}
+
+
+bool findNextChange(BufferView * bv)
+{
+ if (!bv->available())
+ return false;
+
+ bv->hideCursor();
+ bv->update(bv->getLyXText(), BufferView::SELECT | BufferView::FITCUR);
+
+ pos_type length;
+
+ if (bv->theLockingInset()) {
+ bool found = bv->theLockingInset()->nextChange(bv, length);
+
+ // We found the stuff inside the inset so we don't have to
+ // do anything as the inset did all the update for us!
+ if (found)
+ return true;
+
+ // We now are in the main text but if we did a forward
+ // search we have to put the cursor behind the inset.
+ bv->text->cursorRight(bv, true);
+ }
+ // If we arrive here we are in the main text again so we
+ // just start searching from the root LyXText at the position
+ // we are!
+ LyXText * text = bv->text;
+
+ if (text->selection.set())
+ text->cursor = text->selection.end;
+
+ bv->toggleSelection();
+ text->clearSelection();
+
+ SearchResult result = nextChange(bv, text, length);
+
+ lyxerr << "Result is " << result << endl;
+
+ bool found = true;
+
+ // If we found the cursor inside an inset we will get back
+ // SR_FOUND_NOUPDATE and we don't have to do anything as the
+ // inset did it already.
+ if (result == SR_FOUND) {
+ bv->unlockInset(bv->theLockingInset());
+ bv->update(text, BufferView::SELECT|BufferView::FITCUR);
+ text->setSelectionRange(bv, length);
+ bv->toggleSelection(false);
+ bv->update(text, BufferView::SELECT|BufferView::FITCUR);
+ } else if (result == SR_NOT_FOUND) {
+ bv->unlockInset(bv->theLockingInset());
+ bv->update(text, BufferView::SELECT|BufferView::FITCUR);
+ found = false;
+ }
+
+ return found;
+}
+
} // end lyxfind namespace
#endif
#include "LString.h"
+#include "support/types.h"
class BufferView;
class LyXText;
string const & searchstr, bool forward,
bool casesens = true, bool matchwrd = false);
+/// find the next change in the buffer
+bool findNextChange(BufferView * bv);
+
+SearchResult findNextChange(BufferView * bv, LyXText * text, lyx::pos_type & length);
+
+SearchResult nextChange(BufferView * bv, LyXText * text, lyx::pos_type & length);
+
} // end namespace LyXFind
-#endif
+
+#endif // LYXFIND_H
disable = !view()->
isSavedPosition(strToUnsignedInt(ev.argument));
break;
+ case LFUN_MERGE_CHANGES:
+ case LFUN_ACCEPT_CHANGE:
+ case LFUN_REJECT_CHANGE:
+ case LFUN_ACCEPT_ALL_CHANGES:
+ case LFUN_REJECT_ALL_CHANGES:
+ disable = !buf->params.tracking_changes;
+ break;
case LFUN_INSET_TOGGLE: {
LyXText * lt = view()->getLyXText();
disable = !(isEditableInset(lt->getInset())
if (ev.argument == buf->fileName())
flag.setOnOff(true);
break;
+ case LFUN_TRACK_CHANGES:
+ flag.setOnOff(buf->params.tracking_changes);
+ break;
default:
break;
}
-/* This file is part of
- * ======================================================
+/**
+ * \file lyxrc.C
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
*
- * LyX, The Document Processor
- *
- * Copyright 1995 Matthias Ettrich
- * Copyright 1995-2001 The LyX Team.
- *
- * ====================================================== */
+ * Full author contact details are available in file CREDITS
+ */
#include <config.h>
#include "intl.h"
#include "support/path.h"
#include "support/filetools.h"
+#include "support/LAssert.h"
+#include "support/userinfo.h"
#include "converter.h"
#include "gettext.h"
#include "lyxlex.h"
{ "\\use_pspell", LyXRC::RC_USE_PSPELL },
#endif
{ "\\use_tempdir", LyXRC::RC_USETEMPDIR },
+ { "\\user_email", LyXRC::RC_USER_EMAIL },
+ { "\\user_name", LyXRC::RC_USER_NAME },
{ "\\view_dvi_paper_option", LyXRC::RC_VIEWDVI_PAPEROPTION },
{ "\\viewer" ,LyXRC::RC_VIEWER},
{ "\\wheel_jump", LyXRC::RC_WHEEL_JUMP }
// should be moved from the LyXRC class).
use_gui = true;
pdf_mode = false;
+
+ user_name = lyx::user_name();
+
+ user_email = lyx::user_email();
+
+ if (user_email.empty())
+ user_email = _("email address unknown");
}
}
break;
+ case RC_USER_NAME:
+ if (lexrc.next())
+ user_name = lexrc.getString();
+ break;
+
+ case RC_USER_EMAIL:
+ if (lexrc.next())
+ user_email = lexrc.getString();
+ break;
+
case RC_LAST: break; // this is just a dummy
}
}
<< '\n';
}
+ case RC_USER_NAME:
+ os << "\\user_name \"" << user_name << "\"\n";
+
+ case RC_USER_EMAIL:
+ os << "\\user_email " << user_email << "\n";
+
case RC_SHOW_BANNER:
if (show_banner != system_lyxrc.show_banner) {
os << "\\show_banner " << tostr(show_banner) << '\n';
if (!converters.getConverter(cit->from, cit->to))
os << "\\converter \"" << cit->from
<< "\" \"" << cit->to << "\" \"\" \"\"\n";
+
}
os.flush();
}
// -*- C++ -*-
-/* This file is part of
- * ======================================================
+/**
+ * \file lyxrc.h
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
*
- * LyX, The Document Processor
- *
- * Copyright 1995 Matthias Ettrich
- * Copyright 1995-2001 The LyX Team.
- *
- * ====================================================== */
+ * Full author contact details are available in file CREDITS
+ */
#ifndef LYXRC_H
#define LYXRC_H
#ifdef USE_PSPELL
RC_USE_PSPELL,
#endif
+ RC_USER_NAME,
+ RC_USER_EMAIL,
RC_LAST
};
bool preview_hashed_labels;
///
float preview_scale_factor;
+ /// user name
+ string user_name;
+ /// user email
+ string user_email;
private:
/// Is a bind file already (or currently) read?
}
+void Row::top_of_text(unsigned int top)
+{
+ top_of_text_ = top;
+}
+
+
+unsigned int Row::top_of_text() const
+{
+ return top_of_text_;
+}
+
+
void Row::baseline(unsigned int b)
{
baseline_ = b;
///
unsigned short ascent_of_text() const;
///
+ void top_of_text(unsigned int top);
+ ///
+ unsigned int top_of_text() const;
+ ///
void baseline(unsigned int b);
///
unsigned int baseline() const;
unsigned short height_;
///
unsigned int width_;
- ///
+ /// ascent from baseline including prelude space
unsigned short ascent_of_text_;
+ /// the top of the real text in the row
+ unsigned int top_of_text_;
///
unsigned int baseline_;
///
/// returns the inset at cursor (if it exists), 0 otherwise
Inset * getInset() const;
+ /// accept selected change
+ void acceptChange(BufferView * bv);
+
+ /// reject selected change
+ void rejectChange(BufferView * bv);
+
/** 'selects" the next word, where the cursor is not in
and returns this word as string. THe cursor will be moved
to the beginning of this word.
/* these things are for search and replace */
- /** sets the selection over the number of characters of string,
- no check!!
- */
- void setSelectionOverString(BufferView *, string const & str);
+ /**
+ * Sets the selection from the current cursor position to length
+ * characters to the right. No safety checks.
+ */
+ void setSelectionRange(BufferView *, lyx::pos_type length);
/** simple replacing. The font of the first selected character
is used
/// paint the selection background
void paintRowSelection(DrawRowParams & p);
+ /// paint change bar
+ void paintChangeBar(DrawRowParams & p);
+
/// paint appendix marker
void paintRowAppendix(DrawRowParams & p);
#include "encoding.h"
#include "ParameterStruct.h"
#include "gettext.h"
+#include "changes.h"
#include "insets/insetbib.h"
#include "insets/insetoptarg.h"
#include <algorithm>
#include <fstream>
#include <csignal>
+#include <ctime>
using std::ostream;
using std::endl;
LyXFont font1(LyXFont::ALL_INHERIT, bparams.language);
+ Change running_change = Change(Change::UNCHANGED);
+ lyx::time_type const curtime(lyx::current_time());
+
int column = 0;
for (pos_type i = 0; i < size(); ++i) {
if (!i) {
column = 0;
}
+ Change change = pimpl_->lookupChangeFull(i);
+ Changes::lyxMarkChange(os, column, curtime, running_change, change);
+ running_change = change;
+
// Write font changes
LyXFont font2 = getFontSettings(bparams, i);
if (font2 != font1) {
break;
}
}
+
+ // to make reading work properly
+ if (!size()) {
+ running_change = pimpl_->lookupChange(0);
+ Changes::lyxMarkChange(os, column, curtime,
+ Change(Change::UNCHANGED), running_change);
+ }
+ Changes::lyxMarkChange(os, column, curtime,
+ running_change, Change(Change::UNCHANGED));
}
}
+bool Paragraph::erase(pos_type start, pos_type end)
+{
+ return pimpl_->erase(start, end);
+}
+
+
bool Paragraph::checkInsertChar(LyXFont & font)
{
if (pimpl_->inset_owner)
void Paragraph::insertChar(pos_type pos, Paragraph::value_type c,
- LyXFont const & font)
+ LyXFont const & font, Change change)
{
- pimpl_->insertChar(pos, c, font);
+ pimpl_->insertChar(pos, c, font, change);
}
}
-void Paragraph::insertInset(pos_type pos, Inset * inset, LyXFont const & font)
+void Paragraph::insertInset(pos_type pos, Inset * inset, LyXFont const & font, Change change)
{
- pimpl_->insertInset(pos, inset, font);
+ pimpl_->insertInset(pos, inset, font, change);
}
int i = 0;
while (!empty() && (isNewline(0) || isLineSeparator(0))) {
- erase(0);
+ pimpl_->eraseIntern(0);
++i;
}
// Do we have an open font change?
bool open_font = false;
+ Change::Type running_change = Change::UNCHANGED;
+
texrow.start(this, 0);
// if the paragraph is empty, the loop will not be entered at all
running_font = font;
open_font = true;
}
-
+
+ Change::Type change = pimpl_->lookupChange(i);
+
+ column += Changes::latexMarkChange(os, running_change, change);
+ running_change = change;
+
if (c == Paragraph::META_NEWLINE) {
// newlines are handled differently here than
// the default in SimpleTeXSpecialChars().
os, texrow, moving_arg,
font, running_font,
basefont, open_font,
+ running_change,
*style, i, column, c);
}
}
+ column += Changes::latexMarkChange(os,
+ running_change, Change::UNCHANGED);
+
// If we have an open font definition, we have to close it
if (open_font) {
#ifdef FIXED_LANGUAGE_END_DETECTION
}
+void Paragraph::trackChanges(Change::Type type)
+{
+ pimpl_->trackChanges(type);
+}
+
+
+void Paragraph::untrackChanges()
+{
+ pimpl_->untrackChanges();
+}
+
+
+void Paragraph::cleanChanges()
+{
+ pimpl_->cleanChanges();
+}
+
+
+Change::Type Paragraph::lookupChange(lyx::pos_type pos) const
+{
+ lyx::Assert(!size() || pos < size());
+ return pimpl_->lookupChange(pos);
+}
+
+
+Change const Paragraph::lookupChangeFull(lyx::pos_type pos) const
+{
+ lyx::Assert(!size() || pos < size());
+ return pimpl_->lookupChangeFull(pos);
+}
+
+
+bool Paragraph::isChanged(pos_type start, pos_type end) const
+{
+ return pimpl_->isChanged(start, end);
+}
+
+
+bool Paragraph::isChangeEdited(pos_type start, pos_type end) const
+{
+ return pimpl_->isChangeEdited(start, end);
+}
+
+
+void Paragraph::markErased()
+{
+ pimpl_->markErased();
+}
+
+
+void Paragraph::acceptChange(pos_type start, pos_type end)
+{
+ return pimpl_->acceptChange(start, end);
+}
+
+
+void Paragraph::rejectChange(pos_type start, pos_type end)
+{
+ return pimpl_->rejectChange(start, end);
+}
+
+
lyx::pos_type Paragraph::size() const
{
return pimpl_->size();
#include "insets/inset.h" // Just for Inset::Code
#include "support/types.h"
+#include "changes.h"
#include "LString.h"
///
Paragraph const * next() const;
+ /// initialise tracking for this par
+ void trackChanges(Change::Type = Change::UNCHANGED);
+
+ /// stop tracking
+ void untrackChanges();
+
+ /// set entire paragraph to new text for change tracking
+ void cleanChanges();
+
+ /// look up change type at given pos
+ Change::Type lookupChange(lyx::pos_type pos) const;
+
+ /// look up change at given pos
+ Change const lookupChangeFull(lyx::pos_type pos) const;
+
+ /// is there a change within the given range ?
+ bool isChanged(lyx::pos_type start, lyx::pos_type end) const;
+
+ /// is there a non-addition in this range ?
+ bool isChangeEdited(lyx::pos_type start, lyx::pos_type end) const;
+
+ /// accept change
+ void acceptChange(lyx::pos_type start, lyx::pos_type end);
+
+ /// reject change
+ void rejectChange(lyx::pos_type start, lyx::pos_type end);
+
+ /// mark whole par as erased
+ void markErased();
+
///
void previous(Paragraph *);
///
depth_type getMaxDepthAfter() const;
///
void applyLayout(LyXLayout_ptr const & new_layout);
- ///
+
+ /// erase the char at the given position
void erase(lyx::pos_type pos);
- /** Get unistantiated font setting. Returns the difference
+ /// erase the given range. Returns true if actually erased.
+ bool erase(lyx::pos_type start, lyx::pos_type end);
+
+ /** Get uninstantiated font setting. Returns the difference
between the characters font and the layoutfont.
This is what is stored in the fonttable
*/
///
void insertChar(lyx::pos_type pos, value_type c);
///
- void insertChar(lyx::pos_type pos, value_type c, LyXFont const &);
+ void insertChar(lyx::pos_type pos, value_type c, LyXFont const &, Change change = Change(Change::INSERTED));
///
bool checkInsertChar(LyXFont &);
///
void insertInset(lyx::pos_type pos, Inset * inset);
///
- void insertInset(lyx::pos_type pos, Inset * inset, LyXFont const &);
+ void insertInset(lyx::pos_type pos, Inset * inset, LyXFont const &, Change change = Change(Change::INSERTED));
///
bool insetAllowed(Inset::Code code);
///
///
//Counters & counters();
+ friend void breakParagraph(BufferParams const & bparams,
+ Paragraph * par, lyx::pos_type pos, int flag);
+
private:
///
LyXLayout_ptr layout_;
Pimpl * pimpl_;
};
-#endif
+
+inline bool isInsertedText(Paragraph const * par, lyx::pos_type pos)
+{
+ return par->lookupChange(pos) == Change::INSERTED;
+}
+
+
+inline bool isDeletedText(Paragraph const * par, lyx::pos_type pos)
+{
+ return par->lookupChange(pos) == Change::DELETED;
+}
+
+#endif // PARAGRAPH_H
#include <config.h>
#include "paragraph_funcs.h"
+#include "paragraph_pimpl.h"
#include "buffer.h"
#include "ParagraphParameters.h"
#include "lyxtextclasslist.h"
// remember to set the inset_owner
tmp->setInsetOwner(par->inInset());
+ if (bparams.tracking_changes)
+ tmp->trackChanges();
+
// this is an idea for a more userfriendly layout handling, I will
// see what the users say
pos_type pos_end = par->size() - 1;
pos_type i = pos;
pos_type j = pos;
+
for (; i <= pos_end; ++i) {
+ Change::Type change(par->lookupChange(i));
par->cutIntoMinibuffer(bparams, i);
- if (tmp->insertFromMinibuffer(j - pos))
+ if (tmp->insertFromMinibuffer(j - pos)) {
+ tmp->pimpl_->setChange(j - pos, change);
++j;
+ }
}
for (i = pos_end; i >= pos; --i) {
- par->erase(i);
+ par->pimpl_->eraseIntern(i);
}
}
par->setLabelWidthString(tmp->params().labelWidthString());
par->params().depth(tmp->params().depth());
}
+
+ // subtle, but needed to get empty pars working right
+ if (bparams.tracking_changes) {
+ if (!par->size()) {
+ par->cleanChanges();
+ } else if (!tmp->size()) {
+ tmp->cleanChanges();
+ }
+ }
}
id_ = p.id_;
else
id_ = paragraph_id++;
+
+ if (p.tracking())
+ changes_.reset(new Changes(*p.changes_.get()));
}
void Paragraph::Pimpl::clear()
{
text.clear();
+#warning changes ?
}
{
lyx::Assert(par);
text = par->pimpl_->text;
+ if (par->pimpl_->tracking()) {
+ changes_.reset(new Changes(*(par->pimpl_->changes_.get())));
+ }
+}
+
+
+void Paragraph::Pimpl::trackChanges(Change::Type type)
+{
+ if (tracking()) {
+ lyxerr[Debug::CHANGES] << "already tracking for par " << id_ << endl;
+ return;
+ }
+
+ lyxerr[Debug::CHANGES] << "track changes for par "
+ << id_ << " type " << type << endl;
+ changes_.reset(new Changes(type));
+ changes_->set(type, 0, size());
+}
+
+
+void Paragraph::Pimpl::untrackChanges()
+{
+ changes_.reset(0);
+}
+
+
+void Paragraph::Pimpl::cleanChanges()
+{
+ // if we're not tracking, we don't want to reset...
+ if (!tracking())
+ return;
+
+ changes_.reset(new Changes(Change::INSERTED));
+ changes_->set(Change::INSERTED, 0, size());
+}
+
+
+bool Paragraph::Pimpl::isChanged(pos_type start, pos_type end) const
+{
+ if (!tracking())
+ return false;
+
+ return changes_->isChange(start, end);
+}
+
+
+bool Paragraph::Pimpl::isChangeEdited(pos_type start, pos_type end) const
+{
+ if (!tracking())
+ return false;
+
+ return changes_->isChangeEdited(start, end);
+}
+
+
+void Paragraph::Pimpl::setChange(pos_type pos, Change::Type type)
+{
+ if (!tracking())
+ return;
+
+ changes_->set(type, pos);
+}
+
+
+Change::Type Paragraph::Pimpl::lookupChange(pos_type pos) const
+{
+ if (!tracking())
+ return Change::UNCHANGED;
+
+ return changes_->lookup(pos);
+}
+
+
+Change const Paragraph::Pimpl::lookupChangeFull(pos_type pos) const
+{
+ if (!tracking())
+ return Change(Change::UNCHANGED);
+
+ return changes_->lookupFull(pos);
+}
+
+
+void Paragraph::Pimpl::markErased()
+{
+ lyx::Assert(tracking());
+
+ // FIXME: we should actually remove INSERTED chars.
+ // difficult because owning insettexts/tabulars need
+ // to update themselves when rows etc. change
+ changes_->set(Change::DELETED, 0, size());
+ changes_->reset(Change::DELETED);
+}
+
+
+void Paragraph::Pimpl::acceptChange(pos_type start, pos_type end)
+{
+ if (!tracking())
+ return;
+
+ if (!size()) {
+ changes_.reset(new Changes(Change::UNCHANGED));
+ return;
+ }
+
+ lyxerr << "acceptchange" << endl;
+ pos_type i = start;
+
+ for (; i < end; ++i) {
+ switch (lookupChange(i)) {
+ case Change::UNCHANGED:
+ break;
+
+ case Change::INSERTED:
+ changes_->set(Change::UNCHANGED, i);
+ break;
+
+ case Change::DELETED:
+ eraseIntern(i);
+ changes_->erase(i);
+ --end;
+ --i;
+ break;
+ }
+ }
+
+ lyxerr << "endacceptchange" << endl;
+ changes_->reset(Change::UNCHANGED);
}
+void Paragraph::Pimpl::rejectChange(pos_type start, pos_type end)
+{
+ if (!tracking())
+ return;
+
+ if (!size()) {
+ changes_.reset(new Changes(Change::UNCHANGED));
+ return;
+ }
+
+ pos_type i = start;
+
+ for (; i < end; ++i) {
+ switch (lookupChange(i)) {
+ case Change::UNCHANGED:
+ break;
+
+ case Change::INSERTED:
+ eraseIntern(i);
+ changes_->erase(i);
+ --end;
+ --i;
+ break;
+
+ case Change::DELETED:
+ changes_->set(Change::UNCHANGED, i);
+ break;
+ }
+ }
+ changes_->reset(Change::UNCHANGED);
+}
+
+
Paragraph::value_type Paragraph::Pimpl::getChar(pos_type pos) const
{
// This is in the critical path for loading!
void Paragraph::Pimpl::setChar(pos_type pos, value_type c)
{
+#warning changes
text[pos] = c;
}
void Paragraph::Pimpl::insertChar(pos_type pos, value_type c,
- LyXFont const & font)
+ LyXFont const & font, Change change)
{
lyx::Assert(pos <= size());
+ if (tracking()) {
+ changes_->record(change, pos);
+ }
+
// This is actually very common when parsing buffers (and
// maybe inserting ascii text)
if (pos == size()) {
void Paragraph::Pimpl::insertInset(pos_type pos,
- Inset * inset, LyXFont const & font)
+ Inset * inset, LyXFont const & font, Change change)
{
lyx::Assert(inset);
lyx::Assert(pos <= size());
- insertChar(pos, META_INSET, font);
+ insertChar(pos, META_INSET, font, change);
lyx::Assert(text[pos] == META_INSET);
// Add a new entry in the insetlist.
}
-void Paragraph::Pimpl::erase(pos_type pos)
+bool Paragraph::Pimpl::erasePos(pos_type pos)
{
lyx::Assert(pos < size());
+
+ if (tracking()) {
+ Change::Type changetype(changes_->lookup(pos));
+ changes_->record(Change(Change::DELETED), pos);
+
+ // only allow the actual removal if it was /new/ text
+ if (changetype != Change::INSERTED) {
+ if (text[pos] == Paragraph::META_INSET) {
+ Inset * i(owner_->getInset(pos));
+ i->markErased();
+ }
+ return false;
+ }
+ }
+
+ eraseIntern(pos);
+ return true;
+}
+
+
+void Paragraph::Pimpl::eraseIntern(pos_type pos)
+{
// if it is an inset, delete the inset entry
if (text[pos] == Paragraph::META_INSET) {
owner_->insetlist.erase(pos);
}
+void Paragraph::Pimpl::erase(pos_type pos)
+{
+ erasePos(pos);
+}
+
+
+bool Paragraph::Pimpl::erase(pos_type start, pos_type end)
+{
+ pos_type i = start;
+ pos_type count = end - start;
+ bool any_erased = false;
+
+ while (count) {
+ if (!erasePos(i)) {
+ ++i;
+ } else {
+ any_erased = true;
+ }
+ --count;
+ }
+ return any_erased;
+}
+
+
void Paragraph::Pimpl::simpleTeXBlanks(ostream & os, TexRow & texrow,
pos_type const i,
unsigned int & column,
return true;
}
-
+
void Paragraph::Pimpl::simpleTeXSpecialChars(Buffer const * buf,
BufferParams const & bparams,
ostream & os,
LyXFont & running_font,
LyXFont & basefont,
bool & open_font,
+ Change::Type & running_change,
LyXLayout const & style,
pos_type & i,
unsigned int & column,
switch (c) {
case Paragraph::META_INSET: {
Inset * inset = owner_->getInset(i);
- if (inset) {
- bool close = false;
- int const len = os.tellp();
- //ostream::pos_type const len = os.tellp();
- if ((inset->lyxCode() == Inset::GRAPHICS_CODE
- || inset->lyxCode() == Inset::MATH_CODE
- || inset->lyxCode() == Inset::URL_CODE)
- && running_font.isRightToLeft()) {
- os << "\\L{";
- close = true;
- }
+
+ // FIXME: remove this check
+ if (!inset)
+ break;
+
+ if (inset->isTextInset()) {
+ column += Changes::latexMarkChange(os, running_change,
+ Change::UNCHANGED);
+ running_change = Change::UNCHANGED;
+ }
+
+ bool close = false;
+ int const len = os.tellp();
+ //ostream::pos_type const len = os.tellp();
+ if ((inset->lyxCode() == Inset::GRAPHICS_CODE
+ || inset->lyxCode() == Inset::MATH_CODE
+ || inset->lyxCode() == Inset::URL_CODE)
+ && running_font.isRightToLeft()) {
+ os << "\\L{";
+ close = true;
+ }
#ifdef WITH_WARNINGS
#warning Bug: we can have an empty font change here!
// right now, which means stupid latex code like \textsf{}. AFAIK,
// this does not harm dvi output. A minor bug, thus (JMarc)
#endif
- // some insets cannot be inside a font change command
- if (open_font && inset->noFontChange()) {
- column +=running_font.
- latexWriteEndChanges(os,
- basefont,
- basefont);
- open_font = false;
- basefont = owner_->getLayoutFont(bparams);
- running_font = basefont;
- }
+ // some insets cannot be inside a font change command
+ if (open_font && inset->noFontChange()) {
+ column +=running_font.
+ latexWriteEndChanges(os,
+ basefont,
+ basefont);
+ open_font = false;
+ basefont = owner_->getLayoutFont(bparams);
+ running_font = basefont;
+ }
- int tmp = inset->latex(buf, os, moving_arg,
- style.free_spacing);
+ int tmp = inset->latex(buf, os, moving_arg,
+ style.free_spacing);
- if (close)
- os << '}';
+ if (close)
+ os << '}';
- if (tmp) {
- for (int j = 0; j < tmp; ++j) {
- texrow.newline();
- }
- texrow.start(owner_, i + 1);
- column = 0;
- } else {
- column += int(os.tellp()) - len;
+ if (tmp) {
+ for (int j = 0; j < tmp; ++j) {
+ texrow.newline();
}
+ texrow.start(owner_, i + 1);
+ column = 0;
+ } else {
+ column += int(os.tellp()) - len;
}
}
break;
// -*- C++ -*-
-/* This file is part of
- * ======================================================
- *
- * LyX, The Document Processor
- *
- * Copyright 1995 Matthias Ettrich
- * Copyright 1995-2001 The LyX Team.
- *
- * ====================================================== */
+/**
+ * \file paragraph_pimpl.h
+ * Copyright 1995-2002 the LyX Team
+ * Read the file COPYING
+ */
#ifndef PARAGRAPH_PIMPL_H
#define PARAGRAPH_PIMPL_H
#include "paragraph.h"
#include "ParagraphParameters.h"
+#include "changes.h"
#include "counters.h"
+#include <boost/scoped_ptr.hpp>
+
class LyXLayout;
struct Paragraph::Pimpl {
void clear();
///
void setContentsFromPar(Paragraph const * par);
+ /// set tracking mode
+ void trackChanges(Change::Type type = Change::UNCHANGED);
+ /// stop tracking
+ void untrackChanges();
+ /// set all text as new for change mode
+ void cleanChanges();
+ /// look up change type at given pos
+ Change::Type lookupChange(lyx::pos_type pos) const;
+ /// look up change at given pos
+ Change const lookupChangeFull(lyx::pos_type pos) const;
+ /// is there a change in the given range ?
+ bool isChanged(lyx::pos_type start, lyx::pos_type end) const;
+ /// is there a non-addition in this range ?
+ bool isChangeEdited(lyx::pos_type start, lyx::pos_type end) const;
+
+ /// set change at pos
+ void setChange(lyx::pos_type pos, Change::Type type);
+
+ /// mark as erased
+ void markErased();
+
+ /// accept change
+ void acceptChange(lyx::pos_type start, lyx::pos_type end);
+
+ /// reject change
+ void rejectChange(lyx::pos_type start, lyx::pos_type end);
+
+ /// are we tracking changes ?
+ bool tracking() const {
+ return changes_.get();
+ }
+
///
value_type getChar(lyx::pos_type pos) const;
///
void setChar(lyx::pos_type pos, value_type c);
///
- void insertChar(lyx::pos_type pos, value_type c, LyXFont const & font);
- ///
- void insertInset(lyx::pos_type pos, Inset * inset, LyXFont const & font);
+ void insertChar(lyx::pos_type pos, value_type c, LyXFont const & font, Change change = Change(Change::INSERTED));
///
+ void insertInset(lyx::pos_type pos, Inset * inset, LyXFont const & font, Change change = Change(Change::INSERTED));
+ /// definite erase
+ void eraseIntern(lyx::pos_type pos);
+ /// erase the given position
void erase(lyx::pos_type pos);
+ /// erase the given range
+ bool erase(lyx::pos_type start, lyx::pos_type end);
///
LyXFont const realizeFont(LyXFont const & font,
BufferParams const & bparams) const;
typedef std::vector<FontTable> FontList;
///
FontList fontlist;
+
///
Paragraph * TeXDeeper(Buffer const *, BufferParams const &,
std::ostream &, TexRow & texrow);
bool moving_arg,
LyXFont & font, LyXFont & running_font,
LyXFont & basefont, bool & open_font,
+ Change::Type & running_change,
LyXLayout const & style,
lyx::pos_type & i,
unsigned int & column, value_type const c);
ParagraphParameters params;
private:
+ /// erase at the given position. Returns true if it was actually erased
+ bool erasePos(lyx::pos_type pos);
+
/// match a string against a particular point in the paragraph
bool isTextAt(string const & str, lyx::pos_type pos) const;
+ /// for recording and looking up changes in revision tracking mode
+ boost::scoped_ptr<Changes> changes_;
+
/// Who owns us?
Paragraph * owner_;
///
+2003-02-08 John Levon <levon@movementarian.org>
+
+ * Makefile.am:
+ * lyxtime.h:
+ * lyxtime.C: add typedef for time_t, add current_time
+
+ * Makefile.am:
+ * userinfo.h:
+ * userinfo.C: add
+
2002-12-04 Jean-Marc Lasgouttes <Jean-Marc.Lasgouttes@inria.fr>
* filetools.C (getExtFromContents): remove detection of epsi
lyxfunctional.h \
lyxlib.h \
lyxmanip.h \
+ lyxtime.C \
+ lyxtime.h \
$(LYXSTRING) lyxsum.C \
mkdir.C \
nt_defines.h \
sstream.h \
systemcall.C \
systemcall.h \
+ userinfo.C \
+ userinfo.h \
tempname.C \
textutils.h \
translator.h \
--- /dev/null
+/**
+ * \file lyxtime.C
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
+ *
+ * \author John Levon
+ *
+ * Full author contact details are available in file CREDITS
+ */
+
+#include <config.h>
+
+#include "lyxtime.h"
+
+namespace lyx {
+
+time_type current_time()
+{
+ return time(0);
+}
+
+} // namespace lyx
--- /dev/null
+// -*- C++ -*-
+/**
+ * \file lyxtime.h
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
+ *
+ * \author John Levon
+ *
+ * Full author contact details are available in file CREDITS
+ */
+
+#ifndef LYXTIME_H
+#define LYXTIME_H
+
+#include <time.h>
+
+namespace lyx {
+
+typedef time_t time_type;
+
+time_type current_time();
+
+}; // namespace lyx
+
+#endif // LYXTIME_H
--- /dev/null
+/**
+ * \file userinfo.C
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
+ *
+ * \author John Levon
+ *
+ * Full author contact details are available in file CREDITS
+ */
+
+#include <config.h>
+
+#include "userinfo.h"
+#include "LAssert.h"
+#include "filetools.h"
+
+#include <pwd.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+namespace lyx {
+
+string const user_name()
+{
+ struct passwd * pw(getpwuid(geteuid()));
+ lyx::Assert(pw);
+
+ string name = pw->pw_gecos;
+ if (name.empty())
+ name = pw->pw_name;
+ return name;
+}
+
+
+string const user_email()
+{
+ string email = GetEnv("EMAIL_ADDRESS");
+ if (email.empty())
+ email = GetEnv("EMAIL");
+ return email;
+}
+
+
+} // namespace lyx
--- /dev/null
+// -*- C++ -*-
+/**
+ * \file userinfo.h
+ * This file is part of LyX, the document processor.
+ * Licence details can be found in the file COPYING.
+ *
+ * \author John Levon
+ *
+ * Full author contact details are available in file CREDITS
+ */
+
+#ifndef USERINFO_H
+#define USERINFO_H
+
+#include "LString.h"
+
+namespace lyx {
+
+/// return the current user's real name
+string const user_name();
+
+/// return the current user's e-mail address
+string const user_email();
+
+}; // namespace lyx
+
+#endif // USERINFO_H
cell_info = c_info;
++row;
for (int j = 0; j < columns_; ++j) {
- cell_info[row][j].inset.clear();
+ cell_info[row][j].inset.clear(false);
+ if (bp.tracking_changes)
+ cell_info[row][j].inset.markNew(true);
}
#endif
Reinit();
cell_info = c_info;
//++column;
for (int i = 0; i < rows_; ++i) {
- //cell_info[i][column].inset.clear();
- cell_info[i][column + 1].inset.clear();
+ cell_info[i][column + 1].inset.clear(false);
+ if (bp.tracking_changes)
+ cell_info[i][column + 1].inset.markNew(true);
}
Reinit();
}
cellinfo_of_cell(cell+i)->multicolumn = CELL_PART_OF_MULTICOLUMN;
cellinfo_of_cell(cell)->inset.appendParagraphs(buffer->params,
cellinfo_of_cell(cell+i)->inset.paragraph());
- cellinfo_of_cell(cell+i)->inset.clear();
+ cellinfo_of_cell(cell+i)->inset.clear(false);
}
#else
for (number--; number > 0; --number) {
namespace {
+/// top, right, bottom pixel margin
int const PAPER_MARGIN = 20;
+/// margin for changebar
+int const CHANGEBAR_MARGIN = 10;
+/// left margin
+int const LEFT_MARGIN = PAPER_MARGIN + CHANGEBAR_MARGIN;
} // namespace anon
// Returns the paragraph position of the last character in the specified row
pos_type LyXText::rowLast(Row const * row) const
{
+ if (!row->par()->size())
+ return 0;
+
if (!row->next() || row->next()->par() != row->par()) {
return row->par()->size() - 1;
} else {
Inset * ins;
// we have to consider a space on the last position in this case!
if (row->next() && row->par() == row->next()->par() &&
- row->next()->par()->getChar(last+1) == Paragraph::META_INSET &&
- (ins=row->next()->par()->getInset(last+1)) &&
+ row->next()->par()->getChar(last + 1) == Paragraph::META_INSET &&
+ (ins=row->next()->par()->getInset(last + 1)) &&
(ins->needFullRow() || ins->display()))
{
ignore_the_space_on_the_last_position = false;
if (orig_font.language() == p.bv->buffer()->params.language)
return;
- int const y = p.yo + p.row->height() - 1;
+ int const y = p.yo + p.row->baseline() + 1;
p.pain->line(int(orig_x), y, int(p.x), y, LColor::language);
}
{
pos_type pos = vis2log(vpos);
pos_type const last = rowLastPrintable(p.row);
- LyXFont const & orig_font = getFont(p.bv->buffer(), p.row->par(), pos);
+ LyXFont orig_font(getFont(p.bv->buffer(), p.row->par(), pos));
// first character
string str;
unsigned char c = str[0];
str[0] = transformChar(c, p.row->par(), pos);
}
+
+ bool prev_struckout(isDeletedText(p.row->par(), pos));
+ bool prev_newtext(isInsertedText(p.row->par(), pos));
+
++vpos;
// collect as much similar chars as we can
if (!IsPrintableNonspace(c))
break;
+ if (prev_struckout != isDeletedText(p.row->par(), pos))
+ break;
+
+ if (prev_newtext != isInsertedText(p.row->par(), pos))
+ break;
+
if (arabic && Encodings::IsComposeChar_arabic(c))
break;
if (hebrew && Encodings::IsComposeChar_hebrew(c))
++vpos;
}
+ if (prev_struckout) {
+ orig_font.setColor(LColor::strikeout);
+ } else if (prev_newtext) {
+ orig_font.setColor(LColor::newtext);
+ }
+
// Draw text and set the new x position
p.pain->text(int(p.x), p.yo + p.row->baseline(), str, orig_font);
p.x += font_metrics::width(str, orig_font);
if ((row->par()->getChar(row->pos()) == Paragraph::META_INSET) &&
(ins=row->par()->getInset(row->pos())) &&
(ins->needFullRow() || ins->display()))
- return PAPER_MARGIN;
+ return LEFT_MARGIN;
LyXTextClass const & tclass =
bview->buffer()->params.getLyXTextClass();
string parindent = layout->parindent;
- int x = PAPER_MARGIN;
+ int x = LEFT_MARGIN;
x += font_metrics::signedWidth(tclass.leftmargin(), tclass.defaultfont());
LyXLayout_ptr const & layout = firstpar->layout();
- // as max get the first character of this row then it can increes but not
- // decrees the height. Just some point to start with so we don't have to
+ // as max get the first character of this row then it can increase but not
+ // decrease the height. Just some point to start with so we don't have to
// do the assignment below too often.
LyXFont font = getFont(bview->buffer(), par, row_ptr->pos());
LyXFont::FONT_SIZE const tmpsize = font.size();
row_ptr->baseline(maxasc + labeladdon);
height += row_ptr->height();
+
+ row_ptr->top_of_text(row_ptr->baseline() - font_metrics::maxAscent(font));
+
float x = 0;
if (layout->margintype != MARGIN_RIGHT_ADDRESS_BOX) {
float dummy;
void LyXText::breakParagraph(BufferView * bview, char keep_layout)
{
+ // allow only if at start or end, or all previous is new text
+ if (cursor.pos() && cursor.pos() != cursor.par()->size()
+ && cursor.par()->isChangeEdited(0, cursor.pos()))
+ return;
+
LyXTextClass const & tclass =
bview->buffer()->params.getLyXTextClass();
LyXLayout_ptr const & layout = cursor.par()->layout();
}
+void LyXText::acceptChange(BufferView * bv)
+{
+ if (!selection.set() && cursor.par()->size())
+ return;
+
+ bv->hideCursor();
+
+ if (selection.start.par() == selection.end.par()) {
+ LyXCursor & startc = selection.start;
+ LyXCursor & endc = selection.end;
+ setUndo(bv, Undo::INSERT, startc.par(), startc.par()->next());
+ startc.par()->acceptChange(startc.pos(), endc.pos());
+ finishUndo();
+ clearSelection();
+ redoParagraphs(bv, startc, startc.par()->next());
+ setCursorIntern(bv, startc.par(), 0);
+ }
+#warning handle multi par selection
+}
+
+
+void LyXText::rejectChange(BufferView * bv)
+{
+ if (!selection.set() && cursor.par()->size())
+ return;
+
+ bv->hideCursor();
+
+ if (selection.start.par() == selection.end.par()) {
+ LyXCursor & startc = selection.start;
+ LyXCursor & endc = selection.end;
+ setUndo(bv, Undo::INSERT, startc.par(), startc.par()->next());
+ startc.par()->rejectChange(startc.pos(), endc.pos());
+ finishUndo();
+ clearSelection();
+ redoParagraphs(bv, startc, startc.par()->next());
+ setCursorIntern(bv, startc.par(), 0);
+ }
+#warning handle multi par selection
+}
+
+
// This function is only used by the spellchecker for NextWord().
// It doesn't handle LYX_ACCENTs and probably never will.
WordLangTuple const
}
// Now, skip until we have real text (will jump paragraphs)
- while ((cursor.par()->size() > cursor.pos()
- && (!cursor.par()->isLetter(cursor.pos()))
- && (!cursor.par()->isInset(cursor.pos()) ||
- !cursor.par()->getInset(cursor.pos())->allowSpellcheck()))
- || (cursor.par()->size() == cursor.pos()
- && cursor.par()->next()))
- {
- if (cursor.pos() == cursor.par()->size()) {
- cursor.par(cursor.par()->next());
- cursor.pos(0);
- } else
- cursor.pos(cursor.pos() + 1);
- }
+ while (1) {
+ Paragraph * cpar(cursor.par());
+ pos_type const cpos(cursor.pos());
+
+ if (cpos == cpar->size()) {
+ if (cpar->next()) {
+ cursor.par(cpar->next());
+ cursor.pos(0);
+ continue;
+ }
+ break;
+ }
+ bool const is_bad_inset(cpar->isInset(cpos)
+ && !cpar->getInset(cpos)->allowSpellcheck());
+
+ if (cpar->isLetter(cpos) && !isDeletedText(cpar, cpos)
+ && !is_bad_inset)
+ break;
+
+ cursor.pos(cpos + 1);
+ }
+
// now check if we hit an inset so it has to be a inset containing text!
if (cursor.pos() < cursor.par()->size() &&
- cursor.par()->isInset(cursor.pos()))
- {
+ cursor.par()->isInset(cursor.pos())) {
// lock the inset!
cursor.par()->getInset(cursor.pos())->edit(bview);
// now call us again to do the above trick
// and find the end of the word (insets like optional hyphens
// and ligature break are part of a word)
while (cursor.pos() < cursor.par()->size()
- && (cursor.par()->isLetter(cursor.pos())))
+ && cursor.par()->isLetter(cursor.pos())
+ && !isDeletedText(cursor.par(), cursor.pos()))
cursor.pos(cursor.pos() + 1);
// Finally, we copy the word to a string and return it
break;
}
}
+#warning changes
par->setChar(pos, c);
checkParagraph(bview, par, pos);
pos_type tmppos = cursor.pos();
// First decide if it is possible to transpose at all
+
+ if (tmppos == 0 || tmppos == tmppar->size())
+ return;
- // We are at the beginning of a paragraph.
- if (tmppos == 0) return;
-
- // We are at the end of a paragraph.
- if (tmppos == tmppar->size() - 1) return;
+ if (isDeletedText(tmppar, tmppos - 1)
+ || isDeletedText(tmppar, tmppos))
+ return;
unsigned char c1 = tmppar->getChar(tmppos);
unsigned char c2 = tmppar->getChar(tmppos - 1);
- if (c1 != Paragraph::META_INSET
- && c2 != Paragraph::META_INSET) {
- tmppar->setChar(tmppos, c2);
- tmppar->setChar(tmppos - 1, c1);
- }
// We should have an implementation that handles insets
// as well, but that will have to come later. (Lgb)
- checkParagraph(const_cast<BufferView*>(&bview), tmppar, tmppos);
+ if (c1 == Paragraph::META_INSET || c2 == Paragraph::META_INSET)
+ return;
+
+ bool const erased = tmppar->erase(tmppos - 1, tmppos + 1);
+ pos_type const ipos(erased ? tmppos - 1 : tmppos + 1);
+
+ tmppar->insertChar(ipos, c1);
+ tmppar->insertChar(ipos + 1, c2);
+
+ /* fugly */
+ BufferView * bv(const_cast<BufferView*>(&bview));
+
+ checkParagraph(bv, tmppar, tmppos);
}
// The cursor is at the beginning of a paragraph,
// so the the backspace will collapse two paragraphs into one.
+ // but it's not allowed unless it's new
+ if (cursor.par()->isChangeEdited(0, cursor.par()->size()))
+ return;
+
// we may paste some paragraphs
// is it an empty paragraph?
}
+void LyXText::paintChangeBar(DrawRowParams & p)
+{
+ pos_type const start = p.row->pos();
+ pos_type const end = rowLastPrintable(p.row);
+
+ if (!p.row->par()->isChanged(start, end))
+ return;
+
+ int const height = (p.row->next()
+ ? p.row->height() + p.row->next()->top_of_text()
+ : p.row->baseline());
+
+ p.pain->fillRectangle(4, p.yo, 5,
+ height, LColor::changebar);
+}
+
+
void LyXText::paintRowAppendix(DrawRowParams & p)
{
// FIXME: can be just p.width ?
next_depth = p.row->next()->par()->getDepth();
for (Paragraph::depth_type i = 1; i <= depth; ++i) {
- int const x = (PAPER_MARGIN / 5) * i + p.xo;
+ int x = (PAPER_MARGIN / 5) * i + p.xo;
+ // only consider the changebar space if we're drawing outer left
+ if (!p.xo)
+ x += CHANGEBAR_MARGIN;
int const h = p.yo + p.row->height() - 1 - (i - next_depth - 1) * 3;
p.pain->line(x, p.yo, x, h, LColor::depthbar);
LyXFont const font = getLabelFont(buffer, par);
int const size = int(0.75 * font_metrics::maxAscent(font));
int const y = (p.yo + p.row->baseline()) - size;
- int x = is_rtl ? PAPER_MARGIN : ww - PAPER_MARGIN - size;
+ int x = is_rtl ? LEFT_MARGIN : ww - PAPER_MARGIN - size;
if (p.row->fill() <= size)
x += (size - p.row->fill() + 1) * (is_rtl ? -1 : 1);
LyXLayout_ptr const & layout = par->layout();
+ bool running_strikeout = false;
+ bool is_struckout = false;
+ float last_strikeout_x = 0.0;
+
pos_type vpos = p.row->pos();
while (vpos <= last) {
if (p.x > p.bv->workWidth())
break;
pos_type pos = vis2log(vpos);
+
if (p.x + singleWidth(p.bv, par, pos) < 0) {
p.x += singleWidth(p.bv, par, pos);
++vpos;
continue;
}
+
+ is_struckout = isDeletedText(par, pos);
+
+ if (is_struckout && !running_strikeout) {
+ running_strikeout = true;
+ last_strikeout_x = p.x;
+ }
+
+ bool const highly_editable_inset = par->isInset(pos)
+ && isHighlyEditableInset(par->getInset(pos));
+
+ // if we reach the end of a struck out range, paint it
+ // we also don't paint across things like tables
+ if (running_strikeout && (highly_editable_inset || !is_struckout)) {
+ int const middle = p.yo + p.row->top_of_text()
+ + ((p.row->baseline() - p.row->top_of_text()) / 2);
+ p.pain->line(int(last_strikeout_x), middle, int(p.x), middle,
+ LColor::strikeout, Painter::line_solid, Painter::line_thin);
+ running_strikeout = false;
+ }
+
if (main_body > 0 && pos == main_body - 1) {
int const lwidth = font_metrics::width(layout->labelsep,
getLabelFont(buffer, par));
break;
}
}
+
+ // if we reach the end of a struck out range, paint it
+ if (running_strikeout) {
+ int const middle = p.yo + p.row->top_of_text()
+ + ((p.row->baseline() - p.row->top_of_text()) / 2);
+ p.pain->line(int(last_strikeout_x), middle, int(p.x), middle,
+ LColor::strikeout, Painter::line_solid, Painter::line_thin);
+ running_strikeout = false;
+ }
}
// environment depth brackets
paintRowDepthBar(p);
+ // changebar
+ paintChangeBar(p);
+
// draw any stuff wanted for a first row of a paragraph
if (!row->pos()) {
paintFirstRow(p);
!row->par()->isLineSeparator(main_body - 1)))
main_body = 0;
+ // check for empty row
+ if (!row->par()->size()) {
+ x = int(tmpx);
+ return 0;
+ }
+
while (vc <= last && tmpx <= x) {
c = vis2log(vc);
last_tmpx = tmpx;
: false; // If lastrow is false, we don't need to compute
// the value of rtl.
- if (row->pos() > last) // Row is empty?
- c = row->pos();
- else if (lastrow &&
+ if (lastrow &&
((rtl && left_side && vc == row->pos() && x < tmpx - 5) ||
(!rtl && !left_side && vc == last + 1 && x > tmpx + 5)))
c = last + 1;
// cutSelection can invalidate the cursor so we need to set
// it anew. (Lgb)
- cursor = selection.start;
+ // we prefer the end for when tracking changes
+ cursor = selection.end;
// need a valid cursor. (Lgb)
clearSelection();
}
-// sets the selection over the number of characters of string, no check!!
-void LyXText::setSelectionOverString(BufferView * bview, string const & str)
+void LyXText::setSelectionRange(BufferView * bview, lyx::pos_type length)
{
- if (str.empty())
+ if (!length)
return;
selection.cursor = cursor;
- for (string::size_type i = 0; i < str.length(); ++i)
+ while (length--)
cursorRight(bview);
setSelection(bview);
}
pos_type last = rowLastPrintable(old_row);
- if (pos > last + 1) {
+ // None of these should happen, but we're scaredy-cats
+ if (pos > par->size()) {
+ pos = 0;
+ cur.pos(0);
+ } else if (pos > last + 1) {
// This shouldn't happen.
pos = last + 1;
cur.pos(pos);