]> git.lyx.org Git - features.git/commitdiff
The big change tracking patch. Changes from posted version :
authorJohn Levon <levon@movementarian.org>
Sat, 8 Feb 2003 19:18:01 +0000 (19:18 +0000)
committerJohn Levon <levon@movementarian.org>
Sat, 8 Feb 2003 19:18:01 +0000 (19:18 +0000)
1) abstract time_t into lyx::time_type
2) abstrace struct passwd into support/userinfo
3) make authorlist a per-buffer property instead of global

I will look at the paragraph breaking soon, in the meantime I opened a bug on bugzilla.

git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@6074 a592a061-630c-0410-9148-cb99ea01b6c8

86 files changed:
lib/ChangeLog
lib/chkconfig.ltx
lib/configure.m4
lib/ui/default.ui
po/POTFILES.in
src/BufferView.C
src/BufferView.h
src/BufferView_pimpl.C
src/BufferView_pimpl.h
src/ChangeLog
src/CutAndPaste.C
src/CutAndPaste.h
src/DepTable.C
src/LColor.C
src/LColor.h
src/LaTeXFeatures.C
src/LyXAction.C
src/Makefile.am
src/author.C [new file with mode: 0644]
src/author.h [new file with mode: 0644]
src/buffer.C
src/buffer.h
src/bufferlist.C
src/bufferlist.h
src/bufferparams.C
src/bufferparams.h
src/bufferview_funcs.C
src/changes.C [new file with mode: 0644]
src/changes.h [new file with mode: 0644]
src/commandtags.h
src/debug.C
src/debug.h
src/frontends/ChangeLog
src/frontends/Dialogs.h
src/frontends/controllers/ChangeLog
src/frontends/controllers/ControlChanges.C [new file with mode: 0644]
src/frontends/controllers/ControlChanges.h [new file with mode: 0644]
src/frontends/controllers/ControlPrefs.C
src/frontends/controllers/ControlPrefs.h
src/frontends/controllers/Makefile.am
src/frontends/xforms/ChangeLog
src/frontends/xforms/Dialogs.C
src/frontends/xforms/Dialogs2.C
src/frontends/xforms/Dialogs_impl.h
src/frontends/xforms/FormChanges.C [new file with mode: 0644]
src/frontends/xforms/FormChanges.h [new file with mode: 0644]
src/frontends/xforms/FormPreferences.C
src/frontends/xforms/FormPreferences.h
src/frontends/xforms/Makefile.am
src/frontends/xforms/forms/Makefile.am
src/frontends/xforms/forms/form_changes.fd [new file with mode: 0644]
src/frontends/xforms/forms/form_preferences.fd
src/insets/ChangeLog
src/insets/inset.C
src/insets/inset.h
src/insets/insetcollapsable.C
src/insets/insetcollapsable.h
src/insets/insetert.C
src/insets/insetert.h
src/insets/insettabular.C
src/insets/insettabular.h
src/insets/insettext.C
src/insets/insettext.h
src/lyx_main.C
src/lyxfind.C
src/lyxfind.h
src/lyxfunc.C
src/lyxrc.C
src/lyxrc.h
src/lyxrow.C
src/lyxrow.h
src/lyxtext.h
src/paragraph.C
src/paragraph.h
src/paragraph_funcs.C
src/paragraph_pimpl.C
src/paragraph_pimpl.h
src/support/ChangeLog
src/support/Makefile.am
src/support/lyxtime.C [new file with mode: 0644]
src/support/lyxtime.h [new file with mode: 0644]
src/support/userinfo.C [new file with mode: 0644]
src/support/userinfo.h [new file with mode: 0644]
src/tabular.C
src/text.C
src/text2.C

index 324a34e5d2c7f8242a3f98812e16d6561d86e787..ff19f8c258fef980f5542884210559912026601d 100644 (file)
@@ -1,3 +1,11 @@
+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
index 6480354a69492ea92cfc9b236e85c98f791b2aca..cb720f1cf2debc7ca8f851ba59f4edc9f6673869 100644 (file)
 \TestPackage{varioref}
 \TestPackage{prettyref}
 \TestPackage{natbib}
+\TestPackage{dvipost}
 
 % The test for the graphics package is slightly more involved...
 \newcommand\groption{dvips}
index f95f6337f2b9309940d1cbcbb30f73f93ffeee88..c4a27b7901e3e60ee62781c5772f620022f9a0fb 100644 (file)
@@ -199,7 +199,7 @@ fi
 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"
index 4d65bee872766ae6bad1b7c08c34faa8d0ea8cda..82bd51400376fc62663b2233ce651fec68b68fb6 100644 (file)
@@ -80,6 +80,7 @@ Menuset
        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"
@@ -292,6 +293,12 @@ Menuset
        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
 #
index 5fc56ca9c0de91f8832f459b070f46b3fef91d9d..b08fd3dfa01940dbb54a184e1ae523b8594e7c0a 100644 (file)
@@ -1,19 +1,17 @@
-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
@@ -29,13 +27,13 @@ src/frontends/controllers/ControlSearch.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
@@ -47,8 +45,8 @@ src/frontends/qt2/QCommandBuffer.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
@@ -56,8 +54,8 @@ src/frontends/qt2/QGraphics.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
@@ -75,23 +73,24 @@ src/frontends/qt2/QTabularCreate.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
@@ -124,15 +123,14 @@ src/frontends/xforms/FormToc.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
@@ -159,12 +157,15 @@ src/insets/inseturl.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
@@ -173,9 +174,11 @@ src/mathed/formulamacro.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
index 95c03e8450eb9324ab1c77ffb0acff37f8daf540..7e9eec803e7173cda53ffdd0f30cf38768c05db7 100644 (file)
@@ -29,6 +29,7 @@
 #include "lyxlex.h"
 #include "lyxtext.h"
 #include "undo_funcs.h"
+#include "changes.h"
 
 #include "frontends/Alert.h"
 #include "frontends/Dialogs.h"
@@ -154,6 +155,12 @@ bool BufferView::available() const
 }
 
 
+Change const BufferView::getCurrentChange()
+{
+       return pimpl_->getCurrentChange();
+}
+
 void BufferView::beforeChange(LyXText * text)
 {
        pimpl_->beforeChange(text);
@@ -664,7 +671,7 @@ void BufferView::replaceWord(string const & replacestring)
        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) {
index 5a1b44b3a4988911b054c250a07068dbd2fb5ca9..e4da73d12e285c312f785f14f75e6be8acff28df 100644 (file)
@@ -21,6 +21,7 @@
 
 #include <boost/utility.hpp>
 
+class Change;
 class LyXView;
 class LyXText;
 class TeXErrors;
@@ -107,6 +108,9 @@ public:
        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.
index ab27294eca517d1daf0a6210498f8cc2a97f0d30..0178c990b967e713e8826b49f502ae211ab76e07 100644 (file)
@@ -41,6 +41,8 @@
 #include "ParagraphParameters.h"
 #include "undo_funcs.h"
 #include "funcrequest.h"
+#include "iterators.h"
+#include "lyxfind.h"
 
 #include "insets/insetbib.h"
 #include "insets/insettext.h"
@@ -624,6 +626,21 @@ bool BufferView::Pimpl::available() const
 }
 
 
+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();
@@ -915,6 +932,43 @@ void BufferView::Pimpl::MenuInsertLyXFile(string const & filen)
 }
 
 
+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:"
@@ -1249,6 +1303,56 @@ bool BufferView::Pimpl::dispatch(FuncRequest const & ev)
        }
                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;
index 9a77eba226e0e2ab2c203f0bf4d2e319f8e1dddc..ed39aa1cc3a9463db6e6f6c8d30168ce0b716aff 100644 (file)
@@ -23,6 +23,7 @@
 #pragma interface
 #endif
 
+class Change;
 class LyXView;
 class WorkArea;
 class LyXScreen;
@@ -74,6 +75,8 @@ struct BufferView::Pimpl : public boost::signals::trackable {
        void cursorToggle();
        ///
        bool available() const;
+       /// get the change at the cursor position
+       Change const getCurrentChange();
        ///
        void beforeChange(LyXText *);
        ///
@@ -103,6 +106,9 @@ struct BufferView::Pimpl : public boost::signals::trackable {
        ///
        bool dispatch(FuncRequest const & ev);
 private:
+       /// track changes for the document
+       void trackChanges();
+
        ///
        friend class BufferView;
 
index a0f9d3d8318956f4a126e9f7736ec4b24d54df10..6c54f2a6e982a86cba1e8846dbcef9995202394c 100644 (file)
@@ -1,3 +1,95 @@
+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): 
index 5dca9f0cc40da14ca96b594d45ba840a40620103..0f3a66c47aef613aa776324fe549e5395828ee4e 100644 (file)
@@ -14,7 +14,6 @@
 #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;
@@ -58,6 +59,7 @@ extern BufferView * current_view;
 
 namespace {
 
+// FIXME: stupid name 
 Paragraph * buf = 0;
 textclass_type textclass = 0;
 
@@ -90,81 +92,93 @@ bool CutAndPaste::cutSelection(Paragraph * startpar, Paragraph ** endpar,
        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)
 {
@@ -192,6 +206,7 @@ bool CutAndPaste::copySelection(Paragraph * startpar, Paragraph * endpar,
                Paragraph * tmppar = startpar;
                buf = new Paragraph(*tmppar, false);
                Paragraph * tmppar2 = buf;
+               tmppar2->cleanChanges();
 
                while (tmppar != endpar
                       && tmppar->next()) {
@@ -199,6 +214,8 @@ bool CutAndPaste::copySelection(Paragraph * startpar, Paragraph * endpar,
                        tmppar2->next(new Paragraph(*tmppar, false));
                        tmppar2->next()->previous(tmppar2);
                        tmppar2 = tmppar2->next();
+                       // reset change info
+                       tmppar2->cleanChanges();
                }
                tmppar2->next(0);
 
@@ -349,9 +366,7 @@ bool CutAndPaste::pasteSelection(Paragraph ** par, Paragraph ** endpar,
                // 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
@@ -375,10 +390,10 @@ bool CutAndPaste::pasteSelection(Paragraph ** par, Paragraph ** endpar,
                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
index 88a275fd37c619257c61ef9ac5e9f1c50a9ac36e..07a0dc910f691af398d1744e3f797ae87a0ba258 100644 (file)
@@ -24,7 +24,7 @@ class LyXTextClass;
 ///
 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,
index c148a7e8652d8b0bd924f68b921aea3926d74975..8e0997f8cef643a55594c7430c86eb86bbfdae82 100644 (file)
@@ -24,6 +24,7 @@
 #include "support/lyxlib.h"
 #include "support/filetools.h"
 #include "support/lstrings.h"
+#include "support/lyxtime.h"
 
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -79,7 +80,7 @@ void DepTable::insert(string const & fi, bool upd)
 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()) {
@@ -114,7 +115,7 @@ void DepTable::update()
                }
                ++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;
 }
index 6420afde2e7ef896918175d4a6a2c6dba2636383..58c3cf447c7b3c5be909043141caf301dfa7b98e 100644 (file)
@@ -84,6 +84,9 @@ LColor::LColor()
        { 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",
index da75feef92910bc6227432a00ba11eda2e47a67e..3c7ea8e6ec5e691f41b229c840adebb75e76ee55 100644 (file)
@@ -137,6 +137,12 @@ public:
                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
index b91ee8cbdb662ee0ed7821fa3f44faae6ed4a680..bed69c6c1e0217fd2bb13163c802994cc734469c 100644 (file)
@@ -174,10 +174,11 @@ char const * simplefeatures[] = {
        "varioref",
        "prettyref",
        "float",
-       "wasy"
+       "wasy",
+       "dvipost"
 };
 
-const int nb_simplefeatures = sizeof(simplefeatures) / sizeof(char const *);
+int const nb_simplefeatures = sizeof(simplefeatures) / sizeof(char const *);
 
 }
 
@@ -210,10 +211,11 @@ string const LaTeXFeatures::getPackages() 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";
        }
 
index a2304e7018ac76373a61ed72f9d7c7f7dc8e0b82..15fdc2d2ea203a0e3f5e4e70d989384b16e95b3b 100644 (file)
@@ -413,6 +413,12 @@ void LyXAction::init()
                { 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 }
        };
 
index 5318e78ccd5b7bd2d3f3949accb37cb1116f756d..da66e4a807ccb7f0a2a33891a1547206a1531edd 100644 (file)
@@ -87,6 +87,8 @@ lyx_SOURCES = \
        ToolbarDefaults.C \
        ToolbarDefaults.h \
        WordLangTuple.h \
+       author.C \
+       author.h \
        boost.C \
        boost-inst.C \
        box.h \
@@ -100,6 +102,8 @@ lyx_SOURCES = \
        bufferparams.h \
        bufferview_funcs.C \
        bufferview_funcs.h \
+       changes.C \
+       changes.h \
        chset.C \
        chset.h \
        commandtags.h \
diff --git a/src/author.C b/src/author.C
new file mode 100644 (file)
index 0000000..2cbd698
--- /dev/null
@@ -0,0 +1,94 @@
+/**
+ * \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();
+}
diff --git a/src/author.h b/src/author.h
new file mode 100644 (file)
index 0000000..d362d7f
--- /dev/null
@@ -0,0 +1,67 @@
+/**
+ * \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
index 14fd2a462b1d4f7f96e34301b5957b3636776fdf..60ec905f4766c894bbe4ac7b72238c25a9215f37 100644 (file)
@@ -45,6 +45,7 @@
 #include "lyxtextclasslist.h"
 #include "sgml.h"
 #include "paragraph_funcs.h"
+#include "author.h"
 
 #include "frontends/LyXView.h"
 
@@ -97,6 +98,7 @@
 #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>
@@ -148,7 +150,7 @@ extern BufferList bufferlist;
 
 namespace {
 
-const int LYX_FORMAT = 221;
+const int LYX_FORMAT = 222;
 
 } // namespace anon
 
@@ -165,6 +167,9 @@ Buffer::Buffer(string const & file, bool ronly)
        } else {
                tmppath.erase();
        }
+
+       // set initial author
+       authorlist.record(Author(lyxrc.user_name, lyxrc.user_email)); 
 }
 
 
@@ -245,6 +250,12 @@ void Buffer::setReadonly(bool flag)
 }
 
 
+AuthorList & Buffer::authors()
+{
+       return authorlist;
+}
+
+
 /// Update window titles of all users
 // Should work on a list
 void Buffer::updateTitles() const
@@ -282,6 +293,7 @@ string last_inset_read;
 #endif
 int unknown_layouts;
 int unknown_tokens;
+vector<int> author_ids;
 
 } // anon
 
@@ -298,6 +310,7 @@ bool Buffer::readLyXformat2(LyXLex & lex, Paragraph * par)
 {
        unknown_layouts = 0;
        unknown_tokens = 0;
+       author_ids.clear();
 
        int pos = 0;
        Paragraph::depth_type depth = 0;
@@ -390,6 +403,13 @@ bool Buffer::readLyXformat2(LyXLex & lex, Paragraph * par)
 }
 
 
+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,
@@ -408,7 +428,7 @@ Buffer::parseSingleLyXformat2Token(LyXLex & lex, Paragraph *& 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") {
@@ -498,6 +518,8 @@ Buffer::parseSingleLyXformat2Token(LyXLex & lex, Paragraph *& par,
                        else {
                                par = new Paragraph(par);
                                par->layout(params.getLyXTextClass().defaultLayout());
+                               if (params.tracking_changes)
+                                       par->trackChanges();
                        }
                        pos = 0;
                        par->layout(params.getLyXTextClass()[layoutname]);
@@ -576,9 +598,9 @@ Buffer::parseSingleLyXformat2Token(LyXLex & lex, Paragraph *& par,
                                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 "
@@ -589,16 +611,16 @@ Buffer::parseSingleLyXformat2Token(LyXLex & lex, Paragraph *& par,
                } 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;
@@ -756,6 +778,21 @@ Buffer::parseSingleLyXformat2Token(LyXLex & lex, Paragraph *& par,
        } 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)
@@ -929,16 +966,37 @@ Buffer::parseSingleLyXformat2Token(LyXLex & lex, Paragraph *& par,
                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");
@@ -1005,13 +1063,13 @@ void Buffer::insertStringAsLines(Paragraph *& par, pos_type & pos,
                } 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;
@@ -1155,7 +1213,7 @@ void Buffer::readInset(LyXLex & lex, Paragraph *& par,
        }
 
        if (inset) {
-               par->insertInset(pos, inset, font);
+               par->insertInset(pos, inset, font, current_change);
                ++pos;
        }
 }
@@ -1356,6 +1414,15 @@ bool Buffer::writeFile(string const & fname) const
        // 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
@@ -2096,6 +2163,15 @@ void Buffer::makeLaTeXFile(ostream & os,
 
                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)
@@ -3113,6 +3189,11 @@ void Buffer::validate(LaTeXFeatures & features) const
 {
        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");
index b3f1feeb0a528fbf58ff747fdc0197d74a6027e6..e3fc7d69a6aaf80cb00b84badc41ed56dfdc37f4 100644 (file)
@@ -26,6 +26,7 @@
 #include "texrow.h"
 #include "ParagraphList.h"
 #include "paragraph.h"
+#include "author.h"
 
 #include <boost/shared_ptr.hpp>
 
@@ -302,7 +303,14 @@ public:
 
        /// 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;
 
index 10564433367c7c8424de8ab0143dc61ffca6240d..caba200f85ed7b21aae441818d0fb35b9ed3d4b9 100644 (file)
@@ -569,3 +569,13 @@ Buffer * BufferList::loadLyXFile(string const & filename, bool tolastfiles)
 
        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));
+       }
+}
index b24f813d20f721886a41beedc10a901aec016b2f..f9fb6e3b3518e7047e10f8cfc9cc615b58bb2ef3 100644 (file)
@@ -160,6 +160,10 @@ public:
        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,
index 3ccf77567bc15e051102550e737986894c662732..60e458f1e09cd5b8652504739fedd1c01b7086a8 100644 (file)
@@ -54,6 +54,7 @@ BufferParams::BufferParams()
        use_amsmath = false;
        use_natbib = false;
        use_numerical_citations = false;
+       tracking_changes = false;
        secnumdepth = 3;
        tocdepth = 3;
        language = default_language;
@@ -178,6 +179,8 @@ void BufferParams::writeFile(ostream & os) const
                        }
                }
        }
+
+       os << "\\tracking_changes " << tracking_changes << "\n";
 }
 
 
index a77fb04c3ec7cbbd53e59114fd0657287ee32879..5baf5691512a8ad9c40fbf22cc147721ea208abf 100644 (file)
@@ -214,6 +214,8 @@ public:
        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:
index 17b68935572b6290cb6387d2215755f5fb542bd2..35cbf78e8583401f107d4d67f89ae4682576d4f9 100644 (file)
@@ -25,6 +25,8 @@
 #include "language.h"
 #include "gettext.h"
 #include "ParagraphParameters.h"
+#include "author.h"
+#include "changes.h"
 
 #include "frontends/Alert.h"
 
@@ -148,12 +150,30 @@ string const currentState(BufferView * bv)
        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();
diff --git a/src/changes.C b/src/changes.C
new file mode 100644 (file)
index 0000000..3f827db
--- /dev/null
@@ -0,0 +1,526 @@
+/**
+ * \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;
+       }
+}
diff --git a/src/changes.h b/src/changes.h
new file mode 100644 (file)
index 0000000..ac378e5
--- /dev/null
@@ -0,0 +1,150 @@
+/**
+ * \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
index 93ec20391e19ade24bf91fffe1730db26f109ba3..6fd422aee01d73a1fdbc3ba7296533dfa8811b10 100644 (file)
@@ -290,6 +290,12 @@ enum kb_action {
        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 */
 };
 
index 389efd5a85d83608b55b1caa387c268b361bb3c1..6117f282c208c0d39084e3c2a99bd14e4c6e8a1a 100644 (file)
@@ -58,6 +58,7 @@ error_item errorTags[] = {
        { 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")}
 };
 
@@ -73,7 +74,7 @@ Debug::type const Debug::ANY = Debug::type(
        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)
index cb3d87e799be8e3491cc64e050c492ea895bfcd1..8762fbd66fe5c0ec08a5524f02d60ac84a694269 100644 (file)
@@ -72,7 +72,9 @@ struct Debug {
                ///
                INSETTEXT  = (1 << 20),
                ///
-               GRAPHICS   = (1 << 21)
+               GRAPHICS   = (1 << 21),
+               /// change tracking
+               CHANGES    = (1 << 22)
        };
        ///
        static type const ANY;
index e6c9967f3e8330ecaf026e5ac18a0d48f89ea815..6a70d7246b92a5c6104cb80beaf6d81277e9649b 100644 (file)
@@ -1,3 +1,7 @@
+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]
index cb8a929dd4dcfe6619add2950df8a17973a99758..ca96da9be68ed7842d0dbb084371d6d2fcdbe60e 100644 (file)
@@ -126,6 +126,8 @@ public:
        void showLogFile();
        /// display the top-level maths panel
        void showMathPanel();
+       /// show the merge changes dialog
+       void showMergeChanges();
        ///
        void showMinipage(InsetMinipage *);
        ///
index 41907bffd5a1d03797f0dd20c088b4781e1a12f0..e9bae13ade20444f8924096b6296220ea286af20 100644 (file)
@@ -1,3 +1,12 @@
+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.
diff --git a/src/frontends/controllers/ControlChanges.C b/src/frontends/controllers/ControlChanges.C
new file mode 100644 (file)
index 0000000..3d173d6
--- /dev/null
@@ -0,0 +1,82 @@
+/**
+ * \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());
+}
diff --git a/src/frontends/controllers/ControlChanges.h b/src/frontends/controllers/ControlChanges.h
new file mode 100644 (file)
index 0000000..d7d7f38
--- /dev/null
@@ -0,0 +1,49 @@
+// -*- 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
index 660149bc9fa3dd8cf7a9c9dd58a1a0c50553aec2..7179f5a2d6c6f86c447052813d10c01ebaeced23 100644 (file)
@@ -20,6 +20,7 @@
 #include "ViewBase.h"
 
 #include "frontends/LyXView.h"
+#include "bufferlist.h"
 #include "helper_funcs.h"
 #include "gettext.h"
 #include "support/filetools.h"
@@ -29,6 +30,7 @@
 
 extern string system_lyxdir;
 extern string user_lyxdir;
+extern BufferList bufferlist;
 
 using std::endl;
 using std::pair;
@@ -153,3 +155,9 @@ void ControlPrefs::setFormats(Formats const & form)
 {
        formats = form;
 }
+
+
+void ControlPrefs::setCurrentAuthor()
+{
+       bufferlist.setCurrentAuthor(rc_.user_name, rc_.user_email);
+}
index 10d9a74e5cfdc60cb64ebd7eba2b3743b88a4bad..54670051c81c3ff66a465fa29e235c0c7e43193c 100644 (file)
@@ -66,6 +66,9 @@ public:
        /// 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();
index 4f53db5df297f795601f21fdfd3b4a559707a079..437b524a756f402a02d87ec4f698d2f18abe5373 100644 (file)
@@ -31,6 +31,8 @@ libcontrollers_la_SOURCES= \
        ControlButtons.h \
        ControlCharacter.C \
        ControlCharacter.h \
+       ControlChanges.C \
+       ControlChanges.h \
        ControlCitation.C \
        ControlCitation.h \
        ControlCommand.C \
index f15d62ce47edddb20f616c6e18ae26455562b4a2..ddb79904a9d56416d77ad12a79c47ec6b20254c4 100644 (file)
@@ -1,3 +1,18 @@
+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:
index abb14fbe859c55c8dc52cbfc4dcc2898c84d4a22..f4483db43ffcd202724031afed1865c60547b7da 100644 (file)
@@ -48,6 +48,7 @@ Dialogs::Impl::Impl(LyXView & lv, Dialogs & d)
        : aboutlyx(lv, d),
          bibitem(lv, d),
          bibtex(lv, d),
+         changes(lv, d),
          character(lv, d),
          citation(lv, d),
          document(lv, d),
index 0a7f9f4f5819229da9e06fcfbf50ebb4ab8ba885..0a6f298b000686d3e9f266ea3c688124e1d61471 100644 (file)
@@ -37,6 +37,12 @@ void Dialogs::showBibtex(InsetCommand * ic)
 }
 
 
+void Dialogs::showMergeChanges()
+{
+       pimpl_->changes.controller().show();
+}
+
 void Dialogs::showCharacter()
 {
        pimpl_->character.controller().show();
index de03a22563c4ce239076f6eb9d8ad522970d9f9a..342f10130412e1e74ddf3c944dc931b1989d5b8f 100644 (file)
 #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"
@@ -170,6 +174,9 @@ BibitemDialog;
 typedef GUI<ControlBibtex, FormBibtex, NoRepeatedApplyReadOnlyPolicy, xformsBC>
 BibtexDialog;
 
+typedef GUI<ControlChanges, FormChanges, NoRepeatedApplyReadOnlyPolicy, xformsBC>
+ChangesDialog;
 typedef GUI<ControlCharacter, FormCharacter, OkApplyCancelReadOnlyPolicy, xformsBC>
 CharacterDialog;
 
@@ -270,8 +277,9 @@ struct Dialogs::Impl {
 
        AboutlyxDialog      aboutlyx;
        BibitemDialog       bibitem;
-       BibtexDialog        bibtex;
-       CharacterDialog     character;
+        BibtexDialog        bibtex;
+       ChangesDialog       changes;
+        CharacterDialog     character;
        CitationDialog      citation;
        DocumentDialog      document;
        ErrorDialog         error;
diff --git a/src/frontends/xforms/FormChanges.C b/src/frontends/xforms/FormChanges.C
new file mode 100644 (file)
index 0000000..6219672
--- /dev/null
@@ -0,0 +1,81 @@
+/**
+ * \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;
+}
diff --git a/src/frontends/xforms/FormChanges.h b/src/frontends/xforms/FormChanges.h
new file mode 100644 (file)
index 0000000..7bcf7cf
--- /dev/null
@@ -0,0 +1,43 @@
+// -*- 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
index 7647255cb93e2a93dcf207a2304cced5c1bf5701..ef63096b0e3418310744168042e02c7c8d917e66 100644 (file)
@@ -36,6 +36,7 @@
 #include "LColor.h"
 #include "Lsstream.h"
 #include "funcrequest.h"
+#include "author.h"
 
 #include "support/lyxfunctional.h"
 #include "support/lyxmanip.h"
@@ -97,8 +98,9 @@ FormPreferences::FormPreferences()
        : 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)
 {
 }
 
@@ -175,6 +177,7 @@ void FormPreferences::build()
        interface_.build();
        language_.build();
        lnf_misc_.build();
+       identity_.build();
        outputs_misc_.build();
        paths_.build();
        printer_.build();
@@ -212,6 +215,9 @@ void FormPreferences::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,
@@ -277,6 +283,7 @@ void FormPreferences::apply()
        interface_.apply(rc);
        language_.apply(rc);
        lnf_misc_.apply(rc);
+       identity_.apply(rc);
        outputs_misc_.apply(rc);
        paths_.apply(rc);
        printer_.apply(rc);
@@ -371,6 +378,7 @@ void FormPreferences::update()
        interface_.update(rc);
        language_.update(rc);
        lnf_misc_.update(rc);
+       identity_.update(rc);
        outputs_misc_.update(rc);
        paths_.update(rc);
        printer_.update(rc);
@@ -1424,6 +1432,40 @@ bool FormPreferences::Formats::Input()
 }
 
 
+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)
 {}
index eb47395589937605beb710ce7e4c771bed40a9c7..0f235462d285a736d16907f71d354d0571fc5f8a 100644 (file)
@@ -41,6 +41,7 @@ struct FD_preferences_inputs_misc;
 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;
@@ -336,6 +337,29 @@ private:
        ///
        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:
@@ -485,6 +509,8 @@ private:
        ///
        LnFmisc lnf_misc_;
        ///
+       Identity identity_;
+       ///
        OutputsMisc outputs_misc_;
        ///
        Paths paths_;
index 58e403ab427eebb5575005bdd9071c56dec4b34b..be6da8f9bfe7eaa9b805ac0e75e9ecbde45c5eaf 100644 (file)
@@ -66,6 +66,8 @@ libxforms_la_SOURCES = \
        FormBibtex.h \
        FormBrowser.C \
        FormBrowser.h \
+       FormChanges.C \
+       FormChanges.h \
        FormCharacter.C \
        FormCharacter.h \
        FormCitation.C \
index e81a8a7c4f693d2066c18f6ccaed9b23b9346715..615ad924f00d1f20de55e12736ad2098c2c96f0f 100644 (file)
@@ -11,6 +11,7 @@ SRCS =  form_aboutlyx.fd \
        form_bibitem.fd \
        form_bibtex.fd \
        form_browser.fd \
+       form_changes.fd \
        form_character.fd \
        form_citation.fd \
        form_document.fd \
diff --git a/src/frontends/xforms/forms/form_changes.fd b/src/frontends/xforms/forms/form_changes.fd
new file mode 100644 (file)
index 0000000..a9a5cbf
--- /dev/null
@@ -0,0 +1,160 @@
+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: 
+
+==============================
+--------------------
index 0e08ad6790687d6aa85900540d78040cde276419..ef0ce498f35a30ff6bb094899b4020fcb1a3173f 100644 (file)
@@ -3,7 +3,7 @@ Magic: 13000
 Internal Form Definition File
     (do not change)
 
-Number of forms: 14
+Number of forms: 15
 Unit of measure: FL_COORD_PIXEL
 SnapGrid: 5
 
@@ -1222,6 +1222,66 @@ name: choice_display
 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
index 9df1ee800dd52a067f15c423b67f23b183fb1eca..93de8b4043c043d319ce4c248dcc39e3f82f4f34 100644 (file)
@@ -1,3 +1,20 @@
+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:
index 650b6b4510dba4acc9c4e2846d0225c1a17df3b4..a6ae3fd32e2296cf152102bb478d6d16342882a6 100644 (file)
@@ -361,6 +361,14 @@ UpdatableInset::selectNextWordToSpellcheck(BufferView *bv, float & value) const
 }
 
 
+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)
 {
index ef8ccd31b7a90c5771084bc8111f313c462b5bc4..3b6f241f180253cb6c645fcdbe2323ce3b2316aa 100644 (file)
@@ -23,6 +23,7 @@
 #include "LString.h"
 #include "LColor.h"
 #include "frontends/mouse_state.h"
+#include "support/types.h"
 
 class LyXFont;
 class BufferView;
@@ -335,7 +336,7 @@ public:
        ///
        // 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; }
@@ -361,6 +362,9 @@ public:
           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.
         *
@@ -519,16 +523,20 @@ public:
        ///
        // 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
        ///
index 15faa9578a7aab15e06626c69385958b019fb4fe..a3a5380d9bf948828835da8d38156ccf56883be5 100644 (file)
@@ -669,6 +669,24 @@ void InsetCollapsable::setLabel(string const & l) const
 }
 
 
+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)
 {
index 763804af9e48a0ba94b1aa7ec6f78d6fab86e828..5af584aef2df9817310a14f173ce2195ea16a630 100644 (file)
@@ -169,7 +169,7 @@ public:
        ///
        void close(BufferView *) const;
        ///
-       bool allowSpellcheck() { return inset.allowSpellcheck(); }
+       bool allowSpellcheck() const { return inset.allowSpellcheck(); }
        ///
        WordLangTuple const
        selectNextWordToSpellcheck(BufferView *, float &) const;
@@ -181,6 +181,11 @@ public:
        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);
index 58a796d02a5f52b122801187816b0431d90ab5fd..286c4f0a786864a099abf9dd360fdb44a465fc1a 100644 (file)
@@ -351,6 +351,10 @@ int InsetERT::latex(Buffer const *, ostream & os, bool /*fragile*/,
        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:
index f12e30699e56c757717cbd7cddbd3989be1878ba..83c929041f6100547c56084c252b3dfb8195c3ff 100644 (file)
@@ -105,7 +105,7 @@ public:
        ///
        void close(BufferView *) const;
        ///
-       bool allowSpellcheck() { return false; }
+       bool allowSpellcheck() const { return false; }
 
        WordLangTuple const
        selectNextWordToSpellcheck(BufferView *, float &) const;
index 61cc2ea0bb212b1b94f8e5224c2e9a02f3eac3d1..c364d36aaa0c94b1e8e3a887bb8cd07c7e5b8ae9 100644 (file)
@@ -1171,7 +1171,7 @@ Inset::RESULT InsetTabular::localDispatch(FuncRequest const & cmd)
                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:
@@ -2577,13 +2577,14 @@ bool InsetTabular::pasteSelection(BufferView * bv)
                        *(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;
@@ -2606,7 +2607,7 @@ bool InsetTabular::cutSelection()
        }
        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;
@@ -2788,6 +2789,46 @@ void InsetTabular::toggleSelection(BufferView * bv, bool kill_selection)
 }
 
 
+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)
 {
@@ -2911,12 +2952,14 @@ bool InsetTabular::insertAsciiString(BufferView * bv, string const & buf,
                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)
        {
index fc6bc9ea112774f56f91665d26212678e861a3c1..48cd8ca651357612db45e7806a3f8f40ecdd6e12 100644 (file)
@@ -63,6 +63,7 @@ class LyXLex;
 class Painter;
 class BufferView;
 class Buffer;
+class BufferParams;
 class Paragraph;
 
 class InsetTabular : public UpdatableInset {
@@ -208,7 +209,7 @@ public:
        ///
        LyXCursor const & cursor(BufferView *) const;
        ///
-       bool allowSpellcheck() { return true; }
+       bool allowSpellcheck() const { return true; }
        ///
        WordLangTuple const
        selectNextWordToSpellcheck(BufferView *, float & value) const;
@@ -216,6 +217,11 @@ public:
        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);
@@ -316,7 +322,7 @@ private:
        ///
        bool pasteSelection(BufferView *);
        ///
-       bool cutSelection();
+       bool cutSelection(BufferParams const & bp);
        ///
        bool isRightToLeft(BufferView *);
        ///
index 0e39dbc75ef88bdf37838abe698c89e7e156df94..5532b398e679a7dc2d8f4c45d3b93211daa6aa6a 100644 (file)
@@ -146,6 +146,8 @@ InsetText::InsetText(BufferParams const & bp)
 {
        paragraphs.set(new Paragraph);
        paragraphs.begin()->layout(bp.getLyXTextClass().defaultLayout());
+       if (bp.tracking_changes)
+               paragraphs.begin()->trackChanges();
        init();
 }
 
@@ -208,8 +210,18 @@ InsetText::~InsetText()
 }
 
 
-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();
 
@@ -255,8 +267,11 @@ void InsetText::read(Buffer const * buf, LyXLex & lex)
        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();
@@ -2129,9 +2144,24 @@ void InsetText::setParagraphData(Paragraph * p, bool same_id)
 }
 
 
+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();
@@ -2659,6 +2689,36 @@ void InsetText::toggleSelection(BufferView * bv, bool kill_selection)
 }
 
 
+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)
 {
@@ -2681,7 +2741,7 @@ bool InsetText::searchForward(BufferView * bv, string const & str,
                if (bv->lockInset(this))
                        locked = true;
                lt->cursor = cur;
-               lt->setSelectionOverString(bv, str);
+               lt->setSelectionRange(bv, str.length());
                updateLocal(bv, SELECTION, false);
        }
        if (clear)
@@ -2716,7 +2776,7 @@ bool InsetText::searchBackward(BufferView * bv, string const & str,
                if (bv->lockInset(this))
                        locked = true;
                lt->cursor = cur;
-               lt->setSelectionOverString(bv, str);
+               lt->setSelectionRange(bv, str.length());
                updateLocal(bv, SELECTION, false);
        }
        if (clear)
@@ -2776,12 +2836,16 @@ void InsetText::appendParagraphs(BufferParams const & bparams,
        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())
index 38831533a8cc4c83fc7a912365a2c309ed6b6e9f..a0bbd6e6866b607c853f20a1bf3d0531c49ff25d 100644 (file)
@@ -82,8 +82,8 @@ public:
        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 &);
        ///
@@ -220,7 +220,7 @@ public:
        ///
        void paragraph(Paragraph *);
        ///
-       bool allowSpellcheck() { return true; }
+       bool allowSpellcheck() const { return true; }
        ///
        WordLangTuple const
        selectNextWordToSpellcheck(BufferView *, float & value) const;
@@ -228,6 +228,20 @@ public:
        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);
@@ -238,8 +252,9 @@ public:
        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;
 
index 0b0e0802acc1b2a839ead9188544870fd9283a10..7a189991cc7474579f9842a5a28b33ce34435e0f 100644 (file)
@@ -447,7 +447,7 @@ void LyX::init(bool gui)
        // 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");
index 597df35b6a3622b2c8e0695f04b86106ebc66b39..2526f48a9f4dd78a4ef52b09c6edaddc2bcc8064 100644 (file)
 #include "debug.h"
 #include "gettext.h"
 #include "insets/insettext.h"
+#include "changes.h"
 
 using lyx::pos_type;
+using std::endl;
 
 namespace lyxfind {
 
@@ -97,7 +99,7 @@ int LyXReplace(BufferView * bv,
                        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;
                }
@@ -156,7 +158,7 @@ bool LyXFind(BufferView * bv,
        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) {
@@ -312,4 +314,128 @@ SearchResult SearchBackward(BufferView * bv, LyXText * text,
        }
 }
 
+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
index f86b8dec851bc2c29a3823f4aa6d202e38adc0db..7415a466bcdffcdb1163841c749d1e58ad03ff0e 100644 (file)
@@ -7,6 +7,7 @@
 #endif
 
 #include "LString.h"
+#include "support/types.h"
 
 class BufferView;
 class LyXText;
@@ -49,5 +50,13 @@ SearchResult LyXFind(BufferView *, LyXText * text,
                     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
index bd5310b8091affbfb092e71722bb9162fd8acead..23979786b884500f5f0921965fbb6f07b750e2cf 100644 (file)
@@ -462,6 +462,13 @@ FuncStatus LyXFunc::getStatus(FuncRequest const & ev) const
                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())
@@ -624,6 +631,9 @@ FuncStatus LyXFunc::getStatus(FuncRequest const & ev) const
                if (ev.argument == buf->fileName())
                        flag.setOnOff(true);
                break;
+       case LFUN_TRACK_CHANGES:
+               flag.setOnOff(buf->params.tracking_changes);
+               break;
        default:
                break;
        }
index 4db42ebe7e7ae55b8f7b1de09e675e9d6686d09a..0d45db36034bda73990532419ead9c0620b20d92 100644 (file)
@@ -1,12 +1,10 @@
-/* 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>
 
@@ -27,6 +25,8 @@
 #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"
@@ -148,6 +148,8 @@ keyword_item lyxrcTags[] = {
        { "\\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 }
@@ -264,6 +266,13 @@ void LyXRC::setDefaults() {
        // 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");
 }
 
 
@@ -1094,6 +1103,16 @@ int LyXRC::read(string const & filename)
                        }
                        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
                }
        }
@@ -1261,6 +1280,12 @@ void LyXRC::output(ostream & os) const
                           << '\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';
@@ -1798,6 +1823,7 @@ void LyXRC::output(ostream & os) const
                        if (!converters.getConverter(cit->from, cit->to))
                                os << "\\converter \"" << cit->from
                                   << "\" \"" << cit->to << "\" \"\" \"\"\n";
+
        }
        os.flush();
 }
index cf497b8af24e093d2dad02761134e82f8f00a032..6c0648555b5d7410a9c510b2654988adfec606f2 100644 (file)
@@ -1,13 +1,11 @@
 // -*- 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
@@ -127,6 +125,8 @@ enum LyXRCTags {
 #ifdef USE_PSPELL
        RC_USE_PSPELL,
 #endif
+       RC_USER_NAME,
+       RC_USER_EMAIL,
        RC_LAST
 };
 
@@ -357,6 +357,10 @@ enum LyXRCTags {
        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?
index 3dbe39095f9f23fee7db15faa03773d663c1e7fe..804cbc7e99517f640363a1a401522f5710e25df1 100644 (file)
@@ -83,6 +83,18 @@ unsigned short Row::ascent_of_text() const
 }
 
 
+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;
index e2593fdc7c96be7175eafce864e9d8ea99ff81c5..7b6fc124cbf0ff7af35eb5292116276bf31d57f9 100644 (file)
@@ -53,6 +53,10 @@ public:
        ///
        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;
@@ -76,8 +80,10 @@ private:
        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_;
        ///
index e3da0556bc501a4fe67d7a720636ee653c4032b5..7135568767a2716f82850dc97ca23d5b72ad928d 100644 (file)
@@ -281,6 +281,12 @@ public:
        /// 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.
@@ -408,10 +414,11 @@ public:
 
        /* 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
@@ -574,6 +581,9 @@ private:
        /// paint the selection background
        void paintRowSelection(DrawRowParams & p);
 
+       /// paint change bar
+       void paintChangeBar(DrawRowParams & p);
        /// paint appendix marker
        void paintRowAppendix(DrawRowParams & p);
 
index bd4a227814be2b1df0bb51bc9a916fa80c6080a3..c541fd7ea7dd58f4c798e17b599e8b40d5956085 100644 (file)
@@ -28,6 +28,7 @@
 #include "encoding.h"
 #include "ParameterStruct.h"
 #include "gettext.h"
+#include "changes.h"
 
 #include "insets/insetbib.h"
 #include "insets/insetoptarg.h"
@@ -42,6 +43,7 @@
 #include <algorithm>
 #include <fstream>
 #include <csignal>
+#include <ctime>
 
 using std::ostream;
 using std::endl;
@@ -242,6 +244,9 @@ void Paragraph::write(Buffer const * buf, ostream & os,
 
        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) {
@@ -249,6 +254,10 @@ void Paragraph::write(Buffer const * buf, ostream & os,
                        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) {
@@ -312,6 +321,15 @@ void Paragraph::write(Buffer const * buf, ostream & os,
                        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)); 
 }
 
 
@@ -389,6 +407,12 @@ void Paragraph::erase(pos_type pos)
 }
 
 
+bool Paragraph::erase(pos_type start, pos_type end)
+{
+       return pimpl_->erase(start, end);
+}
+
 bool Paragraph::checkInsertChar(LyXFont & font)
 {
        if (pimpl_->inset_owner)
@@ -405,9 +429,9 @@ void Paragraph::insertChar(pos_type pos, Paragraph::value_type c)
 
 
 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);
 }
 
 
@@ -418,9 +442,9 @@ void Paragraph::insertInset(pos_type pos, Inset * inset)
 }
 
 
-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);
 }
 
 
@@ -736,7 +760,7 @@ int Paragraph::stripLeadingSpaces()
 
        int i = 0;
        while (!empty() && (isNewline(0) || isLineSeparator(0))) {
-               erase(0);
+               pimpl_->eraseIntern(0);
                ++i;
        }
 
@@ -1358,6 +1382,8 @@ bool Paragraph::simpleTeXOnePar(Buffer const * buf,
        // 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
@@ -1444,7 +1470,12 @@ bool Paragraph::simpleTeXOnePar(Buffer const * buf,
                        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().
@@ -1474,10 +1505,14 @@ bool Paragraph::simpleTeXOnePar(Buffer const * buf,
                                                      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
@@ -1808,6 +1843,68 @@ void Paragraph::setContentsFromPar(Paragraph * par)
 }
 
 
+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();
index 4f49050a4d28baa83893901f96822c7b1cfa0eee..3d1f8691fd07d8ef48102060e5852fb3f964703a 100644 (file)
@@ -20,6 +20,7 @@
 #include "insets/inset.h" // Just for Inset::Code
 
 #include "support/types.h"
+#include "changes.h"
 
 #include "LString.h"
 
@@ -165,6 +166,36 @@ public:
        ///
        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 *);
        ///
@@ -197,9 +228,13 @@ public:
        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
        */
@@ -234,13 +269,13 @@ public:
        ///
        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);
        ///
@@ -294,6 +329,9 @@ public:
        ///
        //Counters & counters();
 
+       friend void breakParagraph(BufferParams const & bparams,
+                    Paragraph * par, lyx::pos_type pos, int flag);
 private:
        ///
        LyXLayout_ptr layout_;
@@ -312,4 +350,16 @@ private:
        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
index 249e43088e4308c7ec7dda59bc468888d22e3767..18e6855c76fc1ac308fc87834bbd43ee2c4d2b72 100644 (file)
@@ -11,6 +11,7 @@
 #include <config.h>
 
 #include "paragraph_funcs.h"
+#include "paragraph_pimpl.h"
 #include "buffer.h"
 #include "ParagraphParameters.h"
 #include "lyxtextclasslist.h"
@@ -34,6 +35,9 @@ void breakParagraph(BufferParams const & bparams,
        // 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
 
@@ -73,13 +77,17 @@ void breakParagraph(BufferParams const & bparams,
                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);
                }
        }
 
@@ -102,6 +110,15 @@ void breakParagraph(BufferParams const & bparams,
                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();
+               }
+       }
 }
 
 
index 62fd7a49ac9d6985ac7838fb03cbaee75c77f8fa..eef7f3d8fbfd73c7e6b320bfb4e2b7a532757f32 100644 (file)
@@ -74,12 +74,16 @@ Paragraph::Pimpl::Pimpl(Pimpl const & p, Paragraph * owner, bool same_ids)
                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 ? 
 }
 
 
@@ -87,9 +91,169 @@ void Paragraph::Pimpl::setContentsFromPar(Paragraph const * par)
 {
        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!
@@ -109,15 +273,20 @@ Paragraph::value_type Paragraph::Pimpl::getChar(pos_type pos) const
 
 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()) {
@@ -147,12 +316,12 @@ void Paragraph::Pimpl::insertChar(pos_type pos, value_type c,
 
 
 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.
@@ -164,9 +333,31 @@ void Paragraph::Pimpl::insertInset(pos_type pos,
 }
 
 
-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);
@@ -209,6 +400,30 @@ void Paragraph::Pimpl::erase(pos_type 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,
@@ -269,7 +484,7 @@ bool Paragraph::Pimpl::isTextAt(string const & str, pos_type pos) const
        return true;
 }
 
-
 void Paragraph::Pimpl::simpleTeXSpecialChars(Buffer const * buf,
                                             BufferParams const & bparams,
                                             ostream & os,
@@ -279,6 +494,7 @@ void Paragraph::Pimpl::simpleTeXSpecialChars(Buffer const * buf,
                                             LyXFont & running_font,
                                             LyXFont & basefont,
                                             bool & open_font,
+                                            Change::Type & running_change,
                                             LyXLayout const & style,
                                             pos_type & i,
                                             unsigned int & column,
@@ -294,17 +510,27 @@ void Paragraph::Pimpl::simpleTeXSpecialChars(Buffer const * buf,
        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!
@@ -312,32 +538,31 @@ void Paragraph::Pimpl::simpleTeXSpecialChars(Buffer const * buf,
 // 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;
index 7fd48bafadd2c20b774e0f8cb94812e2299ea279..9f03d866369e6c2e8b95b010868595485cd3b00f 100644 (file)
@@ -1,13 +1,9 @@
 // -*- 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 {
@@ -42,16 +41,52 @@ 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;
@@ -115,6 +150,7 @@ struct Paragraph::Pimpl {
        typedef std::vector<FontTable> FontList;
        ///
        FontList fontlist;
+
        ///
        Paragraph * TeXDeeper(Buffer const *, BufferParams const &,
                                 std::ostream &, TexRow & texrow);
@@ -130,6 +166,7 @@ struct Paragraph::Pimpl {
                                   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);
@@ -148,9 +185,15 @@ struct Paragraph::Pimpl {
        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_;
        ///
index b224f382217eac338bffd46094488b8efbbe4f09..c6c17a57829390f1214005911164cb72e70f6e30 100644 (file)
@@ -1,3 +1,13 @@
+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
index 6323abc82164651cfa78252eaa842937bf3182ce..dc457d47482b2f06e67c142f5550cb0012954c68 100644 (file)
@@ -43,6 +43,8 @@ libsupport_la_SOURCES = \
        lyxfunctional.h \
        lyxlib.h \
        lyxmanip.h \
+       lyxtime.C \
+       lyxtime.h \
        $(LYXSTRING) lyxsum.C \
        mkdir.C \
        nt_defines.h \
@@ -58,6 +60,8 @@ libsupport_la_SOURCES = \
        sstream.h \
        systemcall.C \
        systemcall.h \
+       userinfo.C \
+       userinfo.h \
        tempname.C \
        textutils.h \
        translator.h \
diff --git a/src/support/lyxtime.C b/src/support/lyxtime.C
new file mode 100644 (file)
index 0000000..45b0cbd
--- /dev/null
@@ -0,0 +1,22 @@
+/**
+ * \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
diff --git a/src/support/lyxtime.h b/src/support/lyxtime.h
new file mode 100644 (file)
index 0000000..5af9a54
--- /dev/null
@@ -0,0 +1,25 @@
+// -*- 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
diff --git a/src/support/userinfo.C b/src/support/userinfo.C
new file mode 100644 (file)
index 0000000..17b8fc5
--- /dev/null
@@ -0,0 +1,44 @@
+/**
+ * \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
diff --git a/src/support/userinfo.h b/src/support/userinfo.h
new file mode 100644 (file)
index 0000000..4e2042a
--- /dev/null
@@ -0,0 +1,27 @@
+// -*- 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
index c3255f0cea6e84ae581734dedca58498884fac5e..a2cac72c8799463b0ed11de612856f8b1e98d8c2 100644 (file)
@@ -270,7 +270,9 @@ void LyXTabular::AppendRow(BufferParams const & bp, int cell)
        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();
@@ -321,8 +323,9 @@ void LyXTabular::AppendColumn(BufferParams const & bp, int cell)
        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();
 }
@@ -1555,7 +1558,7 @@ void LyXTabular::SetMultiColumn(Buffer const * buffer, int cell, int number)
                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) {
index 4b653f5d14fe09a0579cd44d87647c775a895b35..f76d936bc73381fd189671ad8ef05d74eee5f59a 100644 (file)
@@ -50,7 +50,12 @@ using lyx::pos_type;
 
 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
 
@@ -245,6 +250,9 @@ int LyXText::singleWidth(BufferView * bview, Paragraph * par,
 // 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 {
@@ -260,8 +268,8 @@ pos_type LyXText::rowLastPrintable(Row const * row) const
        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;
@@ -533,7 +541,7 @@ void LyXText::drawForeignMark(DrawRowParams & p, float const orig_x, LyXFont con
        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);
 }
 
@@ -609,7 +617,7 @@ void LyXText::drawChars(DrawRowParams & p, pos_type & vpos,
 {
        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;
@@ -618,6 +626,10 @@ void LyXText::drawChars(DrawRowParams & p, pos_type & vpos,
                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
@@ -627,6 +639,12 @@ void LyXText::drawChars(DrawRowParams & p, pos_type & vpos,
                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))
@@ -641,6 +659,12 @@ void LyXText::drawChars(DrawRowParams & p, pos_type & vpos,
                ++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);
@@ -702,7 +726,7 @@ int LyXText::leftMargin(BufferView * bview, Row const * row) const
        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();
@@ -710,7 +734,7 @@ int LyXText::leftMargin(BufferView * bview, Row const * row) const
 
        string parindent = layout->parindent;
 
-       int x = PAPER_MARGIN;
+       int x = LEFT_MARGIN;
 
        x += font_metrics::signedWidth(tclass.leftmargin(), tclass.defaultfont());
 
@@ -1312,8 +1336,8 @@ void LyXText::setHeightOfRow(BufferView * bview, Row * row_ptr) const
 
        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();
@@ -1578,6 +1602,9 @@ void LyXText::setHeightOfRow(BufferView * bview, Row * row_ptr) const
        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;
@@ -1722,6 +1749,11 @@ void LyXText::breakAgainOneRow(BufferView * bview, Row * row)
 
 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();
@@ -2386,6 +2418,48 @@ bool LyXText::selectWordWhenUnderCursor(BufferView * bview,
 }
 
 
+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
@@ -2419,24 +2493,32 @@ LyXText::selectNextWordToSpellcheck(BufferView * bview, float & value) 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
@@ -2459,7 +2541,8 @@ LyXText::selectNextWordToSpellcheck(BufferView * bview, float & value) const
        // 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
@@ -2624,6 +2707,7 @@ void LyXText::changeRegionCase(BufferView * bview,
                                break;
                        }
                }
+#warning changes
                par->setChar(pos, c);
                checkParagraph(bview, par, pos);
 
@@ -2646,24 +2730,32 @@ void LyXText::transposeChars(BufferView & bview)
        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);
 }
 
 
@@ -2717,6 +2809,10 @@ void LyXText::backspace(BufferView * bview)
                // 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?
@@ -3188,6 +3284,23 @@ void LyXText::paintRowSelection(DrawRowParams & p)
 }
 
 
+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 ?
@@ -3216,7 +3329,10 @@ void LyXText::paintRowDepthBar(DrawRowParams & p)
                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);
@@ -3572,7 +3688,7 @@ void LyXText::paintLastRow(DrawRowParams & p)
                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);
@@ -3622,16 +3738,42 @@ void LyXText::paintRowText(DrawRowParams & p)
 
        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));
@@ -3681,6 +3823,15 @@ void LyXText::paintRowText(DrawRowParams & p)
                                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;
+       }
 }
 
 
@@ -3725,6 +3876,9 @@ void LyXText::getVisibleRow(BufferView * bv, int y_offset, int x_offset,
        // environment depth brackets
        paintRowDepthBar(p);
 
+       // changebar
+       paintChangeBar(p);
        // draw any stuff wanted for a first row of a paragraph
        if (!row->pos()) {
                paintFirstRow(p);
@@ -3778,6 +3932,12 @@ LyXText::getColumnNearX(BufferView * bview, Row * row, int & x,
             !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;
@@ -3822,9 +3982,7 @@ LyXText::getColumnNearX(BufferView * bview, Row * row, int & x,
                : 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;
index a4592c79ce95685b2e809ba6452775bba85d109d..16c056c7b6d603483af90cf51ad2ec0044176fed 100644 (file)
@@ -1493,7 +1493,8 @@ void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
 
        // 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();
@@ -1557,14 +1558,13 @@ void LyXText::pasteSelection(BufferView * bview)
 }
 
 
-// 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);
 }
@@ -1801,7 +1801,11 @@ void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
 
        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);