2003-08-23 00:17:00 +00:00
|
|
|
|
/*
|
2007-04-26 04:41:58 +00:00
|
|
|
|
* \file CutAndPaste.cpp
|
2003-05-06 09:34:56 +00:00
|
|
|
|
* This file is part of LyX, the document processor.
|
|
|
|
|
* Licence details can be found in the file COPYING.
|
2002-03-21 17:27:08 +00:00
|
|
|
|
*
|
2003-09-07 01:45:40 +00:00
|
|
|
|
* \author J<EFBFBD>rgen Vigna
|
2003-05-06 09:34:56 +00:00
|
|
|
|
* \author Lars Gullik Bj<EFBFBD>nnes
|
2003-06-28 01:23:11 +00:00
|
|
|
|
* \author Alfredo Braunstein
|
2006-11-23 22:20:38 +00:00
|
|
|
|
* \author Michael Gerz
|
2002-03-21 17:27:08 +00:00
|
|
|
|
*
|
2003-08-23 00:17:00 +00:00
|
|
|
|
* Full author contact details are available in file CREDITS.
|
2003-05-06 09:34:56 +00:00
|
|
|
|
*/
|
2000-04-10 14:29:05 +00:00
|
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
|
|
#include "CutAndPaste.h"
|
2003-09-06 18:38:02 +00:00
|
|
|
|
|
2007-04-26 04:41:58 +00:00
|
|
|
|
#include "Buffer.h"
|
2004-03-25 09:16:36 +00:00
|
|
|
|
#include "buffer_funcs.h"
|
2007-04-26 04:41:58 +00:00
|
|
|
|
#include "BufferParams.h"
|
2007-04-26 14:56:30 +00:00
|
|
|
|
#include "Cursor.h"
|
2004-04-18 07:32:34 +00:00
|
|
|
|
#include "debug.h"
|
2007-04-26 04:41:58 +00:00
|
|
|
|
#include "ErrorList.h"
|
|
|
|
|
#include "FuncRequest.h"
|
2003-09-06 18:38:02 +00:00
|
|
|
|
#include "gettext.h"
|
2007-04-26 04:41:58 +00:00
|
|
|
|
#include "InsetIterator.h"
|
|
|
|
|
#include "Language.h"
|
2004-04-18 07:32:34 +00:00
|
|
|
|
#include "lfuns.h"
|
2007-04-26 04:41:58 +00:00
|
|
|
|
#include "LyXFunc.h"
|
|
|
|
|
#include "LyXRC.h"
|
2007-04-29 23:33:02 +00:00
|
|
|
|
#include "Text.h"
|
2007-04-29 19:53:54 +00:00
|
|
|
|
#include "TextClassList.h"
|
2007-04-26 04:41:58 +00:00
|
|
|
|
#include "Paragraph.h"
|
2002-08-20 19:41:13 +00:00
|
|
|
|
#include "paragraph_funcs.h"
|
2003-09-06 18:38:02 +00:00
|
|
|
|
#include "ParagraphParameters.h"
|
2007-04-26 04:41:58 +00:00
|
|
|
|
#include "ParIterator.h"
|
|
|
|
|
#include "Undo.h"
|
2003-09-06 18:38:02 +00:00
|
|
|
|
|
2007-04-25 01:24:38 +00:00
|
|
|
|
#include "insets/InsetCharStyle.h"
|
|
|
|
|
#include "insets/InsetTabular.h"
|
2000-04-10 14:29:05 +00:00
|
|
|
|
|
2007-04-26 16:06:39 +00:00
|
|
|
|
#include "mathed/MathData.h"
|
2006-09-17 09:14:18 +00:00
|
|
|
|
#include "mathed/InsetMath.h"
|
|
|
|
|
#include "mathed/MathSupport.h"
|
2004-04-18 07:32:34 +00:00
|
|
|
|
|
2003-05-13 09:48:57 +00:00
|
|
|
|
#include "support/lstrings.h"
|
2002-12-01 21:10:37 +00:00
|
|
|
|
|
2006-07-03 20:19:33 +00:00
|
|
|
|
#include "frontends/Clipboard.h"
|
2007-01-07 14:44:44 +00:00
|
|
|
|
#include "frontends/Selection.h"
|
2006-07-03 20:19:33 +00:00
|
|
|
|
|
2007-02-22 10:04:49 +00:00
|
|
|
|
#include <boost/current_function.hpp>
|
2004-03-25 09:16:36 +00:00
|
|
|
|
#include <boost/tuple/tuple.hpp>
|
|
|
|
|
|
2007-01-16 14:31:07 +00:00
|
|
|
|
#include <string>
|
|
|
|
|
|
2004-04-18 07:32:34 +00:00
|
|
|
|
using std::endl;
|
2003-05-01 12:53:22 +00:00
|
|
|
|
using std::for_each;
|
2003-09-08 00:33:41 +00:00
|
|
|
|
using std::make_pair;
|
|
|
|
|
using std::pair;
|
2003-06-17 15:33:49 +00:00
|
|
|
|
using std::vector;
|
2003-10-06 15:43:21 +00:00
|
|
|
|
using std::string;
|
2003-05-01 12:53:22 +00:00
|
|
|
|
|
2003-06-17 15:33:49 +00:00
|
|
|
|
|
2007-01-16 14:45:12 +00:00
|
|
|
|
namespace lyx {
|
|
|
|
|
|
|
|
|
|
using support::bformat;
|
|
|
|
|
using frontend::Clipboard;
|
|
|
|
|
|
2001-03-20 01:22:46 +00:00
|
|
|
|
namespace {
|
|
|
|
|
|
2006-10-21 00:16:43 +00:00
|
|
|
|
typedef std::pair<pit_type, int> PitPosPair;
|
2004-03-25 09:16:36 +00:00
|
|
|
|
|
|
|
|
|
typedef limited_stack<pair<ParagraphList, textclass_type> > CutStack;
|
|
|
|
|
|
2004-04-18 07:32:34 +00:00
|
|
|
|
CutStack theCuts(10);
|
2007-02-02 03:10:15 +00:00
|
|
|
|
// persistent selection, cleared until the next selection
|
|
|
|
|
CutStack selectionBuffer(1);
|
2000-04-10 14:29:05 +00:00
|
|
|
|
|
2005-09-06 17:39:39 +00:00
|
|
|
|
// store whether the tabular stack is newer than the normal copy stack
|
2006-04-05 23:56:29 +00:00
|
|
|
|
// FIXME: this is a workaround for bug 1919. Should be removed for 1.5,
|
2005-09-06 17:39:39 +00:00
|
|
|
|
// when we (hopefully) have a one-for-all paste mechanism.
|
2007-01-04 11:00:09 +00:00
|
|
|
|
bool dirty_tabular_stack_ = false;
|
2005-09-06 17:39:39 +00:00
|
|
|
|
|
2000-04-11 22:55:29 +00:00
|
|
|
|
|
2004-04-18 07:32:34 +00:00
|
|
|
|
void region(CursorSlice const & i1, CursorSlice const & i2,
|
2007-04-29 13:39:47 +00:00
|
|
|
|
Inset::row_type & r1, Inset::row_type & r2,
|
|
|
|
|
Inset::col_type & c1, Inset::col_type & c2)
|
2004-04-18 07:32:34 +00:00
|
|
|
|
{
|
2007-04-29 13:39:47 +00:00
|
|
|
|
Inset & p = i1.inset();
|
2004-04-18 07:32:34 +00:00
|
|
|
|
c1 = p.col(i1.idx());
|
|
|
|
|
c2 = p.col(i2.idx());
|
|
|
|
|
if (c1 > c2)
|
|
|
|
|
std::swap(c1, c2);
|
|
|
|
|
r1 = p.row(i1.idx());
|
|
|
|
|
r2 = p.row(i2.idx());
|
|
|
|
|
if (r1 > r2)
|
|
|
|
|
std::swap(r1, r2);
|
|
|
|
|
}
|
2003-06-28 01:23:11 +00:00
|
|
|
|
|
2004-03-25 09:16:36 +00:00
|
|
|
|
|
2004-04-18 07:32:34 +00:00
|
|
|
|
bool checkPastePossible(int index)
|
|
|
|
|
{
|
|
|
|
|
return size_t(index) < theCuts.size() && !theCuts[index].first.empty();
|
|
|
|
|
}
|
2004-03-25 09:16:36 +00:00
|
|
|
|
|
2004-04-18 07:32:34 +00:00
|
|
|
|
|
2005-07-16 15:22:07 +00:00
|
|
|
|
pair<PitPosPair, pit_type>
|
2007-04-26 14:56:30 +00:00
|
|
|
|
pasteSelectionHelper(Cursor & cur, ParagraphList const & parlist,
|
2006-09-19 09:39:17 +00:00
|
|
|
|
textclass_type textclass, ErrorList & errorlist)
|
2004-03-25 09:16:36 +00:00
|
|
|
|
{
|
2006-09-19 09:39:17 +00:00
|
|
|
|
Buffer const & buffer = cur.buffer();
|
|
|
|
|
pit_type pit = cur.pit();
|
|
|
|
|
pos_type pos = cur.pos();
|
|
|
|
|
ParagraphList & pars = cur.text()->paragraphs();
|
|
|
|
|
|
2005-11-29 15:08:35 +00:00
|
|
|
|
if (parlist.empty())
|
2005-07-16 15:22:07 +00:00
|
|
|
|
return make_pair(PitPosPair(pit, pos), pit);
|
|
|
|
|
|
|
|
|
|
BOOST_ASSERT (pos <= pars[pit].size());
|
|
|
|
|
|
|
|
|
|
// Make a copy of the CaP paragraphs.
|
2005-11-29 15:08:35 +00:00
|
|
|
|
ParagraphList insertion = parlist;
|
|
|
|
|
textclass_type const tc = buffer.params().textclass;
|
2005-07-16 15:22:07 +00:00
|
|
|
|
|
|
|
|
|
// Now remove all out of the pars which is NOT allowed in the
|
|
|
|
|
// new environment and set also another font if that is required.
|
|
|
|
|
|
|
|
|
|
// Convert newline to paragraph break in ERT inset.
|
|
|
|
|
// This should not be here!
|
|
|
|
|
if (pars[pit].inInset() &&
|
Add support for listings package. Two listings command \lstinline, \lstinputlisting and an environment \lstlisting are supported, along with preamble \lstset. \lstinputlisting is implemented through Include dialog, and the other two are implemented with a new inset listings, along with its dialog.
* src/LyXAction.cpp: listing-insert action
* src/insets/Inset.h,cpp: LISTINGS_CODE
* src/insets/InsetInclude.cpp: handle \lstinputlisting
* src/insets/InsetListings.h,cpp: new listings inset
* src/insets/InsetListingsParams.h,cpp: parameters from listings package
* src/insets/InsetCommandParams.h,cpp: handle lstinputlisting option
* src/Bidi.cpp: handle LISTINGS_CODE
* src/frontends/qt4/ui/TextLayoutUi.ui: update UI
* src/frontends/qt4/ui/ListingsUi.ui: new dialog
* src/frontends/qt4/ui/IncludeUi.ui: update UI
* src/frontends/qt4/QInclude.h,cpp: add lstinputlisting
* src/frontends/qt4/QDocument.h,cpp: add textedit for preamble listings_params
* src/frontends/qt4/QListings.h,cpp: new listings inset
* src/frontends/qt4/Dialogs.cpp: new listings dialog
* src/frontends/controllers/ControlInclude.h,cpp: add lstinputlisting
* src/frontends/controllers/ControlListings.h,cpp: new listings inset
* src/LyXFunc.cpp: handle LISTING_CODE
* src/Paragraph.cpp: handle LISTING_CODE
* src/factory.cpp: new listings inset
* src/CutAndPaste.cpp: handle LISTINGS_CODE
* src/LaTeXFeatures.cpp: require listings
* src/Text3.cpp: Handle LISTINGS_CODE
* src/lfuns.h: add LFUN_LISTING_INSERT
* src/Buffer.cpp: change lyx file format to 269
* src/BufferParams.h,cpp: add listings_params to preamble
* lib/lyx2lyx/LyX.py: lyx2lyx
* lib/lyx2lyx/lyx_1_5.py: lyx2lyx
* lib/ui/stdmenus.inc: new menu item (no shortcut!)
* src/insets/Makefile.am: update autotools
* src/frontends/controllers/Makefile.am
* src/frontends/qt4/Makefile.dialogs
* src/frontends/qt4/Makefile.am
* po/POTFILES.in: a few more translatable files.
* development/scons/scons_manifest.py: scons build system
* development/FORMAT: document format changes
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@18243 a592a061-630c-0410-9148-cb99ea01b6c8
2007-05-09 19:11:42 +00:00
|
|
|
|
(pars[pit].inInset()->lyxCode() == Inset::ERT_CODE ||
|
|
|
|
|
pars[pit].inInset()->lyxCode() == Inset::LISTINGS_CODE)) {
|
2005-07-16 15:22:07 +00:00
|
|
|
|
for (ParagraphList::size_type i = 0; i < insertion.size(); ++i) {
|
|
|
|
|
for (pos_type j = 0; j < insertion[i].size(); ++j) {
|
|
|
|
|
if (insertion[i].isNewline(j)) {
|
2006-10-19 17:46:50 +00:00
|
|
|
|
// do not track deletion of newline
|
2006-10-21 17:05:20 +00:00
|
|
|
|
insertion[i].eraseChar(j, false);
|
2005-07-16 15:22:07 +00:00
|
|
|
|
breakParagraphConservative(
|
|
|
|
|
buffer.params(),
|
|
|
|
|
insertion, i, j);
|
2005-02-03 17:24:40 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2005-07-16 15:22:07 +00:00
|
|
|
|
}
|
2005-02-03 17:24:40 +00:00
|
|
|
|
|
2006-09-19 09:39:17 +00:00
|
|
|
|
// If we are in an inset which returns forceDefaultParagraphs,
|
|
|
|
|
// set the paragraphs to default
|
|
|
|
|
if (cur.inset().forceDefaultParagraphs(cur.idx())) {
|
2007-04-29 18:58:28 +00:00
|
|
|
|
Layout_ptr const layout =
|
2007-04-29 19:53:54 +00:00
|
|
|
|
buffer.params().getTextClass().defaultLayout();
|
2006-09-19 09:39:17 +00:00
|
|
|
|
ParagraphList::iterator const end = insertion.end();
|
|
|
|
|
for (ParagraphList::iterator par = insertion.begin();
|
|
|
|
|
par != end; ++par)
|
|
|
|
|
par->layout(layout);
|
|
|
|
|
}
|
|
|
|
|
|
2005-07-16 15:22:07 +00:00
|
|
|
|
// Make sure there is no class difference.
|
2006-05-21 17:33:03 +00:00
|
|
|
|
InsetText in;
|
|
|
|
|
// This works without copying any paragraph data because we have
|
|
|
|
|
// a specialized swap method for ParagraphList. This is important
|
|
|
|
|
// since we store pointers to insets at some places and we don't
|
|
|
|
|
// want to invalidate them.
|
|
|
|
|
insertion.swap(in.paragraphs());
|
2006-10-21 00:16:43 +00:00
|
|
|
|
cap::switchBetweenClasses(textclass, tc, in, errorlist);
|
2006-05-21 17:33:03 +00:00
|
|
|
|
insertion.swap(in.paragraphs());
|
2005-07-16 15:22:07 +00:00
|
|
|
|
|
|
|
|
|
ParagraphList::iterator tmpbuf = insertion.begin();
|
|
|
|
|
int depth_delta = pars[pit].params().depth() - tmpbuf->params().depth();
|
|
|
|
|
|
2006-10-21 00:16:43 +00:00
|
|
|
|
depth_type max_depth = pars[pit].getMaxDepthAfter();
|
2005-07-16 15:22:07 +00:00
|
|
|
|
|
|
|
|
|
for (; tmpbuf != insertion.end(); ++tmpbuf) {
|
|
|
|
|
// If we have a negative jump so that the depth would
|
|
|
|
|
// go below 0 depth then we have to redo the delta to
|
|
|
|
|
// this new max depth level so that subsequent
|
|
|
|
|
// paragraphs are aligned correctly to this paragraph
|
|
|
|
|
// at level 0.
|
|
|
|
|
if (int(tmpbuf->params().depth()) + depth_delta < 0)
|
|
|
|
|
depth_delta = 0;
|
|
|
|
|
|
|
|
|
|
// Set the right depth so that we are not too deep or shallow.
|
|
|
|
|
tmpbuf->params().depth(tmpbuf->params().depth() + depth_delta);
|
|
|
|
|
if (tmpbuf->params().depth() > max_depth)
|
|
|
|
|
tmpbuf->params().depth(max_depth);
|
|
|
|
|
|
|
|
|
|
// Only set this from the 2nd on as the 2nd depends
|
|
|
|
|
// for maxDepth still on pit.
|
|
|
|
|
if (tmpbuf != insertion.begin())
|
|
|
|
|
max_depth = tmpbuf->getMaxDepthAfter();
|
|
|
|
|
|
|
|
|
|
// Set the inset owner of this paragraph.
|
|
|
|
|
tmpbuf->setInsetOwner(pars[pit].inInset());
|
|
|
|
|
for (pos_type i = 0; i < tmpbuf->size(); ++i) {
|
|
|
|
|
if (tmpbuf->getChar(i) == Paragraph::META_INSET &&
|
|
|
|
|
!pars[pit].insetAllowed(tmpbuf->getInset(i)->lyxCode()))
|
2006-10-19 17:46:50 +00:00
|
|
|
|
// do not track deletion of invalid insets
|
2006-10-21 17:05:20 +00:00
|
|
|
|
tmpbuf->eraseChar(i--, false);
|
2004-03-25 09:16:36 +00:00
|
|
|
|
}
|
2006-04-13 16:47:57 +00:00
|
|
|
|
|
2006-11-07 18:27:03 +00:00
|
|
|
|
tmpbuf->setChange(Change(buffer.params().trackChanges ?
|
|
|
|
|
Change::INSERTED : Change::UNCHANGED));
|
2005-07-16 15:22:07 +00:00
|
|
|
|
}
|
2004-03-25 09:16:36 +00:00
|
|
|
|
|
2005-07-16 15:22:07 +00:00
|
|
|
|
bool const empty = pars[pit].empty();
|
|
|
|
|
if (!empty) {
|
|
|
|
|
// Make the buf exactly the same layout as the cursor
|
|
|
|
|
// paragraph.
|
|
|
|
|
insertion.begin()->makeSameLayout(pars[pit]);
|
|
|
|
|
}
|
2004-03-25 09:16:36 +00:00
|
|
|
|
|
2005-07-16 15:22:07 +00:00
|
|
|
|
// Prepare the paragraphs and insets for insertion.
|
|
|
|
|
// A couple of insets store buffer references so need updating.
|
2006-05-21 17:33:03 +00:00
|
|
|
|
insertion.swap(in.paragraphs());
|
2003-06-17 15:33:49 +00:00
|
|
|
|
|
2005-07-16 15:22:07 +00:00
|
|
|
|
ParIterator fpit = par_iterator_begin(in);
|
|
|
|
|
ParIterator fend = par_iterator_end(in);
|
2004-04-18 07:32:34 +00:00
|
|
|
|
|
2005-07-16 15:22:07 +00:00
|
|
|
|
for (; fpit != fend; ++fpit) {
|
2006-11-25 23:22:34 +00:00
|
|
|
|
InsetList::const_iterator lit = fpit->insetlist.begin();
|
|
|
|
|
InsetList::const_iterator eit = fpit->insetlist.end();
|
2004-04-18 07:32:34 +00:00
|
|
|
|
|
2005-07-16 15:22:07 +00:00
|
|
|
|
for (; lit != eit; ++lit) {
|
|
|
|
|
switch (lit->inset->lyxCode()) {
|
2007-04-29 13:39:47 +00:00
|
|
|
|
case Inset::TABULAR_CODE: {
|
2005-07-16 15:22:07 +00:00
|
|
|
|
InsetTabular * it = static_cast<InsetTabular*>(lit->inset);
|
|
|
|
|
it->buffer(&buffer);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2004-04-18 07:32:34 +00:00
|
|
|
|
|
2005-07-16 15:22:07 +00:00
|
|
|
|
default:
|
|
|
|
|
break; // nothing
|
2004-04-18 07:32:34 +00:00
|
|
|
|
}
|
2003-06-18 13:53:11 +00:00
|
|
|
|
}
|
2005-07-16 15:22:07 +00:00
|
|
|
|
}
|
2006-05-21 17:33:03 +00:00
|
|
|
|
insertion.swap(in.paragraphs());
|
2005-07-16 15:22:07 +00:00
|
|
|
|
|
|
|
|
|
// Split the paragraph for inserting the buf if necessary.
|
|
|
|
|
if (!empty)
|
|
|
|
|
breakParagraphConservative(buffer.params(), pars, pit, pos);
|
|
|
|
|
|
|
|
|
|
// Paste it!
|
|
|
|
|
if (empty) {
|
2006-03-23 20:11:06 +00:00
|
|
|
|
pars.insert(boost::next(pars.begin(), pit),
|
|
|
|
|
insertion.begin(),
|
2006-04-05 23:56:29 +00:00
|
|
|
|
insertion.end());
|
2005-07-16 15:22:07 +00:00
|
|
|
|
|
|
|
|
|
// merge the empty par with the last par of the insertion
|
|
|
|
|
mergeParagraph(buffer.params(), pars,
|
2006-04-05 23:56:29 +00:00
|
|
|
|
pit + insertion.size() - 1);
|
2005-07-16 15:22:07 +00:00
|
|
|
|
} else {
|
2006-03-23 20:11:06 +00:00
|
|
|
|
pars.insert(boost::next(pars.begin(), pit + 1),
|
|
|
|
|
insertion.begin(),
|
2006-04-05 23:56:29 +00:00
|
|
|
|
insertion.end());
|
2005-07-16 15:22:07 +00:00
|
|
|
|
|
|
|
|
|
// merge the first par of the insertion with the current par
|
|
|
|
|
mergeParagraph(buffer.params(), pars, pit);
|
|
|
|
|
}
|
2004-04-18 07:32:34 +00:00
|
|
|
|
|
2005-07-16 15:22:07 +00:00
|
|
|
|
pit_type last_paste = pit + insertion.size() - 1;
|
|
|
|
|
|
|
|
|
|
// Store the new cursor position.
|
|
|
|
|
pit = last_paste;
|
|
|
|
|
pos = pars[last_paste].size();
|
|
|
|
|
|
2006-03-16 06:54:08 +00:00
|
|
|
|
// Join (conditionally) last pasted paragraph with next one, i.e.,
|
|
|
|
|
// the tail of the spliced document paragraph
|
2005-07-16 15:22:07 +00:00
|
|
|
|
if (!empty && last_paste + 1 != pit_type(pars.size())) {
|
|
|
|
|
if (pars[last_paste + 1].hasSameLayout(pars[last_paste])) {
|
|
|
|
|
mergeParagraph(buffer.params(), pars, last_paste);
|
|
|
|
|
} else if (pars[last_paste + 1].empty()) {
|
|
|
|
|
pars[last_paste + 1].makeSameLayout(pars[last_paste]);
|
|
|
|
|
mergeParagraph(buffer.params(), pars, last_paste);
|
|
|
|
|
} else if (pars[last_paste].empty()) {
|
|
|
|
|
pars[last_paste].makeSameLayout(pars[last_paste + 1]);
|
|
|
|
|
mergeParagraph(buffer.params(), pars, last_paste);
|
2004-04-18 07:32:34 +00:00
|
|
|
|
} else {
|
2007-01-14 18:27:27 +00:00
|
|
|
|
pars[last_paste + 1].stripLeadingSpaces(buffer.params().trackChanges);
|
2005-07-16 15:22:07 +00:00
|
|
|
|
++last_paste;
|
2004-04-18 07:32:34 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2005-07-16 15:22:07 +00:00
|
|
|
|
return make_pair(PitPosPair(pit, pos), last_paste + 1);
|
2003-06-17 15:33:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2004-04-18 07:32:34 +00:00
|
|
|
|
PitPosPair eraseSelectionHelper(BufferParams const & params,
|
|
|
|
|
ParagraphList & pars,
|
2004-11-24 21:53:46 +00:00
|
|
|
|
pit_type startpit, pit_type endpit,
|
2004-03-25 09:16:36 +00:00
|
|
|
|
int startpos, int endpos, bool doclear)
|
2001-04-17 13:48:09 +00:00
|
|
|
|
{
|
2006-04-05 23:56:29 +00:00
|
|
|
|
// Start of selection is really invalid.
|
2004-11-24 21:53:46 +00:00
|
|
|
|
if (startpit == pit_type(pars.size()) ||
|
2004-03-27 01:18:51 +00:00
|
|
|
|
(startpos > pars[startpit].size()))
|
2003-04-30 10:32:01 +00:00
|
|
|
|
return PitPosPair(endpit, endpos);
|
2003-03-04 09:27:27 +00:00
|
|
|
|
|
2006-04-05 23:56:29 +00:00
|
|
|
|
// Start and end is inside same paragraph
|
2006-10-23 20:08:18 +00:00
|
|
|
|
if (endpit == pit_type(pars.size()) || startpit == endpit) {
|
|
|
|
|
endpos -= pars[startpit].eraseChars(startpos, endpos, params.trackChanges);
|
2003-04-30 10:32:01 +00:00
|
|
|
|
return PitPosPair(endpit, endpos);
|
2003-04-29 09:40:49 +00:00
|
|
|
|
}
|
2003-03-04 09:27:27 +00:00
|
|
|
|
|
2006-03-11 13:31:41 +00:00
|
|
|
|
for (pit_type pit = startpit; pit != endpit + 1;) {
|
2006-11-23 21:46:39 +00:00
|
|
|
|
pos_type const left = (pit == startpit ? startpos : 0);
|
2007-05-14 03:07:00 +00:00
|
|
|
|
pos_type right = (pit == endpit ? endpos : pars[pit].size() + 1);
|
|
|
|
|
// FIXME: this is a quick fix for bug 3600. It stops a crash but the problem
|
|
|
|
|
// still remains unsolved (e.g. the second example in the bug report).
|
|
|
|
|
// c.f. http://bugzilla.lyx.org/show_bug.cgi?id=3600
|
|
|
|
|
if (right > pars[pit].size() + 1)
|
|
|
|
|
right = pars[pit].size() + 1;
|
2006-11-23 22:20:38 +00:00
|
|
|
|
|
2006-11-24 21:22:57 +00:00
|
|
|
|
bool const merge = pars[pit].isMergedOnEndOfParDeletion(params.trackChanges);
|
|
|
|
|
|
2006-11-23 22:20:38 +00:00
|
|
|
|
// Logically erase only, including the end-of-paragraph character
|
|
|
|
|
pars[pit].eraseChars(left, right, params.trackChanges);
|
|
|
|
|
|
|
|
|
|
// Separate handling of paragraph break:
|
2006-03-11 13:31:41 +00:00
|
|
|
|
if (merge && pit != endpit &&
|
2006-11-23 21:46:39 +00:00
|
|
|
|
(pit + 1 != endpit || pars[pit].hasSameLayout(pars[pit + 1]))) {
|
2006-03-11 13:31:41 +00:00
|
|
|
|
pos_type const thissize = pars[pit].size();
|
|
|
|
|
if (doclear)
|
2007-01-14 18:27:27 +00:00
|
|
|
|
pars[pit + 1].stripLeadingSpaces(params.trackChanges);
|
2006-03-11 13:31:41 +00:00
|
|
|
|
mergeParagraph(params, pars, pit);
|
|
|
|
|
--endpit;
|
|
|
|
|
if (pit == endpit)
|
|
|
|
|
endpos += thissize;
|
2006-04-05 23:56:29 +00:00
|
|
|
|
} else
|
2006-03-11 13:31:41 +00:00
|
|
|
|
++pit;
|
2000-05-22 11:08:25 +00:00
|
|
|
|
}
|
2003-02-08 19:18:01 +00:00
|
|
|
|
|
2006-03-11 13:31:41 +00:00
|
|
|
|
// Ensure legal cursor pos:
|
|
|
|
|
endpit = startpit;
|
|
|
|
|
endpos = startpos;
|
2003-04-30 10:32:01 +00:00
|
|
|
|
return PitPosPair(endpit, endpos);
|
2000-04-10 14:29:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2003-03-04 09:27:27 +00:00
|
|
|
|
|
2007-01-13 18:29:50 +00:00
|
|
|
|
void putClipboard(ParagraphList const & paragraphs, textclass_type textclass,
|
|
|
|
|
docstring const & plaintext)
|
|
|
|
|
{
|
2007-02-19 20:08:11 +00:00
|
|
|
|
// For some strange reason gcc 3.2 and 3.3 do not accept
|
|
|
|
|
// Buffer buffer(string(), false);
|
|
|
|
|
Buffer buffer("", false);
|
2007-01-13 18:29:50 +00:00
|
|
|
|
buffer.setUnnamed(true);
|
|
|
|
|
buffer.paragraphs() = paragraphs;
|
|
|
|
|
buffer.params().textclass = textclass;
|
|
|
|
|
std::ostringstream lyx;
|
|
|
|
|
if (buffer.write(lyx))
|
|
|
|
|
theClipboard().put(lyx.str(), plaintext);
|
|
|
|
|
else
|
|
|
|
|
theClipboard().put(string(), plaintext);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-04-20 08:42:01 +00:00
|
|
|
|
void copySelectionHelper(Buffer const & buf, ParagraphList & pars,
|
2004-11-24 21:53:46 +00:00
|
|
|
|
pit_type startpit, pit_type endpit,
|
2007-02-02 03:10:15 +00:00
|
|
|
|
int start, int end, textclass_type tc, CutStack & cutstack)
|
2001-04-17 13:48:09 +00:00
|
|
|
|
{
|
2004-03-25 09:16:36 +00:00
|
|
|
|
BOOST_ASSERT(0 <= start && start <= pars[startpit].size());
|
|
|
|
|
BOOST_ASSERT(0 <= end && end <= pars[endpit].size());
|
2003-09-09 17:25:35 +00:00
|
|
|
|
BOOST_ASSERT(startpit != endpit || start <= end);
|
2002-03-21 17:27:08 +00:00
|
|
|
|
|
2003-05-01 12:53:22 +00:00
|
|
|
|
// Clone the paragraphs within the selection.
|
2007-02-09 10:27:41 +00:00
|
|
|
|
ParagraphList copy_pars(boost::next(pars.begin(), startpit),
|
|
|
|
|
boost::next(pars.begin(), endpit + 1));
|
2005-07-19 13:17:01 +00:00
|
|
|
|
|
2007-04-26 08:30:11 +00:00
|
|
|
|
// Remove the end of the last paragraph; afterwards, remove the
|
|
|
|
|
// beginning of the first paragraph. Keep this order - there may only
|
|
|
|
|
// be one paragraph! Do not track deletions here; this is an internal
|
|
|
|
|
// action not visible to the user
|
|
|
|
|
|
2007-02-09 10:27:41 +00:00
|
|
|
|
Paragraph & back = copy_pars.back();
|
|
|
|
|
back.eraseChars(end, back.size(), false);
|
|
|
|
|
Paragraph & front = copy_pars.front();
|
|
|
|
|
front.eraseChars(0, start, false);
|
|
|
|
|
|
|
|
|
|
ParagraphList::iterator it = copy_pars.begin();
|
|
|
|
|
ParagraphList::iterator it_end = copy_pars.end();
|
2006-11-07 18:27:03 +00:00
|
|
|
|
|
|
|
|
|
for (; it != it_end; it++) {
|
|
|
|
|
// ERT paragraphs have the Language latex_language.
|
|
|
|
|
// This is invalid outside of ERT, so we need to change it
|
|
|
|
|
// to the buffer language.
|
Add support for listings package. Two listings command \lstinline, \lstinputlisting and an environment \lstlisting are supported, along with preamble \lstset. \lstinputlisting is implemented through Include dialog, and the other two are implemented with a new inset listings, along with its dialog.
* src/LyXAction.cpp: listing-insert action
* src/insets/Inset.h,cpp: LISTINGS_CODE
* src/insets/InsetInclude.cpp: handle \lstinputlisting
* src/insets/InsetListings.h,cpp: new listings inset
* src/insets/InsetListingsParams.h,cpp: parameters from listings package
* src/insets/InsetCommandParams.h,cpp: handle lstinputlisting option
* src/Bidi.cpp: handle LISTINGS_CODE
* src/frontends/qt4/ui/TextLayoutUi.ui: update UI
* src/frontends/qt4/ui/ListingsUi.ui: new dialog
* src/frontends/qt4/ui/IncludeUi.ui: update UI
* src/frontends/qt4/QInclude.h,cpp: add lstinputlisting
* src/frontends/qt4/QDocument.h,cpp: add textedit for preamble listings_params
* src/frontends/qt4/QListings.h,cpp: new listings inset
* src/frontends/qt4/Dialogs.cpp: new listings dialog
* src/frontends/controllers/ControlInclude.h,cpp: add lstinputlisting
* src/frontends/controllers/ControlListings.h,cpp: new listings inset
* src/LyXFunc.cpp: handle LISTING_CODE
* src/Paragraph.cpp: handle LISTING_CODE
* src/factory.cpp: new listings inset
* src/CutAndPaste.cpp: handle LISTINGS_CODE
* src/LaTeXFeatures.cpp: require listings
* src/Text3.cpp: Handle LISTINGS_CODE
* src/lfuns.h: add LFUN_LISTING_INSERT
* src/Buffer.cpp: change lyx file format to 269
* src/BufferParams.h,cpp: add listings_params to preamble
* lib/lyx2lyx/LyX.py: lyx2lyx
* lib/lyx2lyx/lyx_1_5.py: lyx2lyx
* lib/ui/stdmenus.inc: new menu item (no shortcut!)
* src/insets/Makefile.am: update autotools
* src/frontends/controllers/Makefile.am
* src/frontends/qt4/Makefile.dialogs
* src/frontends/qt4/Makefile.am
* po/POTFILES.in: a few more translatable files.
* development/scons/scons_manifest.py: scons build system
* development/FORMAT: document format changes
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@18243 a592a061-630c-0410-9148-cb99ea01b6c8
2007-05-09 19:11:42 +00:00
|
|
|
|
if (it->ownerCode() == Inset::ERT_CODE || it->ownerCode() == Inset::LISTINGS_CODE) {
|
2006-11-07 18:27:03 +00:00
|
|
|
|
it->changeLanguage(buf.params(), latex_language,
|
|
|
|
|
buf.getLanguage());
|
|
|
|
|
}
|
|
|
|
|
it->setInsetOwner(0);
|
|
|
|
|
}
|
2002-03-21 17:27:08 +00:00
|
|
|
|
|
2007-02-09 10:27:41 +00:00
|
|
|
|
// do not copy text (also nested in insets) which is marked as deleted
|
2007-04-29 23:33:02 +00:00
|
|
|
|
// acceptChanges() is defined for Text rather than ParagraphList
|
|
|
|
|
// Thus we must wrap copy_pars into a Text object and cross our fingers
|
|
|
|
|
Text lt;
|
2007-02-09 10:27:41 +00:00
|
|
|
|
copy_pars.swap(lt.paragraphs());
|
|
|
|
|
lt.acceptChanges(buf.params());
|
|
|
|
|
copy_pars.swap(lt.paragraphs());
|
2003-05-01 12:53:22 +00:00
|
|
|
|
|
2007-02-09 10:27:41 +00:00
|
|
|
|
cutstack.push(make_pair(copy_pars, tc));
|
2000-04-10 14:29:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2004-04-18 07:32:34 +00:00
|
|
|
|
} // namespace anon
|
2003-05-01 15:43:58 +00:00
|
|
|
|
|
2003-05-06 09:34:56 +00:00
|
|
|
|
|
2005-07-16 15:22:07 +00:00
|
|
|
|
|
|
|
|
|
|
2004-04-18 07:32:34 +00:00
|
|
|
|
namespace cap {
|
2003-05-01 15:43:58 +00:00
|
|
|
|
|
2007-04-26 14:56:30 +00:00
|
|
|
|
docstring grabAndEraseSelection(Cursor & cur)
|
2004-04-18 07:32:34 +00:00
|
|
|
|
{
|
|
|
|
|
if (!cur.selection())
|
2006-10-22 10:15:23 +00:00
|
|
|
|
return docstring();
|
|
|
|
|
docstring res = grabSelection(cur);
|
2004-04-18 07:32:34 +00:00
|
|
|
|
eraseSelection(cur);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
2003-05-01 13:12:43 +00:00
|
|
|
|
|
|
|
|
|
|
2006-04-09 00:26:19 +00:00
|
|
|
|
void switchBetweenClasses(textclass_type c1, textclass_type c2,
|
2006-05-21 17:33:03 +00:00
|
|
|
|
InsetText & in, ErrorList & errorlist)
|
2004-04-18 07:32:34 +00:00
|
|
|
|
{
|
2006-05-21 17:33:03 +00:00
|
|
|
|
BOOST_ASSERT(!in.paragraphs().empty());
|
2004-04-18 07:32:34 +00:00
|
|
|
|
if (c1 == c2)
|
2005-05-04 11:21:14 +00:00
|
|
|
|
return;
|
2002-03-21 17:27:08 +00:00
|
|
|
|
|
2007-04-29 19:53:54 +00:00
|
|
|
|
TextClass const & tclass1 = textclasslist[c1];
|
|
|
|
|
TextClass const & tclass2 = textclasslist[c2];
|
2003-03-17 16:25:00 +00:00
|
|
|
|
|
2005-05-04 11:21:14 +00:00
|
|
|
|
// layouts
|
2004-04-18 07:32:34 +00:00
|
|
|
|
ParIterator end = par_iterator_end(in);
|
|
|
|
|
for (ParIterator it = par_iterator_begin(in); it != end; ++it) {
|
|
|
|
|
string const name = it->layout()->name();
|
|
|
|
|
bool hasLayout = tclass2.hasLayout(name);
|
2003-06-03 15:10:14 +00:00
|
|
|
|
|
2004-04-18 07:32:34 +00:00
|
|
|
|
if (hasLayout)
|
|
|
|
|
it->layout(tclass2[name]);
|
|
|
|
|
else
|
|
|
|
|
it->layout(tclass2.defaultLayout());
|
2003-06-10 14:39:45 +00:00
|
|
|
|
|
2004-04-18 07:32:34 +00:00
|
|
|
|
if (!hasLayout && name != tclass1.defaultLayoutName()) {
|
2006-09-11 08:54:10 +00:00
|
|
|
|
docstring const s = bformat(
|
|
|
|
|
_("Layout had to be changed from\n%1$s to %2$s\n"
|
|
|
|
|
"because of class conversion from\n%3$s to %4$s"),
|
2006-10-21 00:16:43 +00:00
|
|
|
|
from_utf8(name), from_utf8(it->layout()->name()),
|
|
|
|
|
from_utf8(tclass1.name()), from_utf8(tclass2.name()));
|
2004-04-18 07:32:34 +00:00
|
|
|
|
// To warn the user that something had to be done.
|
2006-09-11 08:54:10 +00:00
|
|
|
|
errorlist.push_back(ErrorItem(_("Changed Layout"), s,
|
2004-04-18 07:32:34 +00:00
|
|
|
|
it->id(), 0,
|
|
|
|
|
it->size()));
|
2003-06-03 15:10:14 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2005-05-04 11:21:14 +00:00
|
|
|
|
|
|
|
|
|
// character styles
|
|
|
|
|
InsetIterator const i_end = inset_iterator_end(in);
|
|
|
|
|
for (InsetIterator it = inset_iterator_begin(in); it != i_end; ++it) {
|
2007-04-29 13:39:47 +00:00
|
|
|
|
if (it->lyxCode() == Inset::CHARSTYLE_CODE) {
|
2005-05-04 11:21:14 +00:00
|
|
|
|
InsetCharStyle & inset =
|
|
|
|
|
static_cast<InsetCharStyle &>(*it);
|
|
|
|
|
string const name = inset.params().type;
|
|
|
|
|
CharStyles::iterator const found_cs =
|
|
|
|
|
tclass2.charstyle(name);
|
|
|
|
|
if (found_cs == tclass2.charstyles().end()) {
|
|
|
|
|
// The character style is undefined in tclass2
|
|
|
|
|
inset.setUndefined();
|
2006-09-11 08:54:10 +00:00
|
|
|
|
docstring const s = bformat(_(
|
2005-05-04 11:21:14 +00:00
|
|
|
|
"Character style %1$s is "
|
|
|
|
|
"undefined because of class "
|
2006-09-11 08:54:10 +00:00
|
|
|
|
"conversion from\n%2$s to %3$s"),
|
2006-10-21 00:16:43 +00:00
|
|
|
|
from_utf8(name), from_utf8(tclass1.name()),
|
|
|
|
|
from_utf8(tclass2.name()));
|
2005-05-04 11:21:14 +00:00
|
|
|
|
// To warn the user that something had to be done.
|
|
|
|
|
errorlist.push_back(ErrorItem(
|
2006-09-11 08:54:10 +00:00
|
|
|
|
_("Undefined character style"),
|
|
|
|
|
s, it.paragraph().id(), it.pos(), it.pos() + 1));
|
2005-05-04 11:21:14 +00:00
|
|
|
|
} else if (inset.undefined()) {
|
|
|
|
|
// The character style is undefined in
|
|
|
|
|
// tclass1 and is defined in tclass2
|
|
|
|
|
inset.setDefined(found_cs);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2004-04-18 07:32:34 +00:00
|
|
|
|
}
|
2003-05-01 15:43:58 +00:00
|
|
|
|
|
2004-04-03 08:37:12 +00:00
|
|
|
|
|
2006-10-11 19:40:50 +00:00
|
|
|
|
std::vector<docstring> const availableSelections(Buffer const & buffer)
|
2004-04-18 07:32:34 +00:00
|
|
|
|
{
|
2006-10-11 19:40:50 +00:00
|
|
|
|
vector<docstring> selList;
|
2003-05-01 14:45:04 +00:00
|
|
|
|
|
2004-04-18 07:32:34 +00:00
|
|
|
|
CutStack::const_iterator cit = theCuts.begin();
|
|
|
|
|
CutStack::const_iterator end = theCuts.end();
|
|
|
|
|
for (; cit != end; ++cit) {
|
|
|
|
|
// we do not use cit-> here because gcc 2.9x does not
|
|
|
|
|
// like it (JMarc)
|
|
|
|
|
ParagraphList const & pars = (*cit).first;
|
2006-10-11 19:40:50 +00:00
|
|
|
|
docstring asciiSel;
|
2004-04-18 07:32:34 +00:00
|
|
|
|
ParagraphList::const_iterator pit = pars.begin();
|
|
|
|
|
ParagraphList::const_iterator pend = pars.end();
|
|
|
|
|
for (; pit != pend; ++pit) {
|
|
|
|
|
asciiSel += pit->asString(buffer, false);
|
|
|
|
|
if (asciiSel.size() > 25) {
|
2006-10-11 19:40:50 +00:00
|
|
|
|
asciiSel.replace(22, docstring::npos,
|
2006-10-21 00:16:43 +00:00
|
|
|
|
from_ascii("..."));
|
2004-04-18 07:32:34 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
2004-03-28 19:13:11 +00:00
|
|
|
|
}
|
2004-03-25 09:16:36 +00:00
|
|
|
|
|
2004-04-18 07:32:34 +00:00
|
|
|
|
selList.push_back(asciiSel);
|
|
|
|
|
}
|
2004-03-25 09:16:36 +00:00
|
|
|
|
|
2004-04-18 07:32:34 +00:00
|
|
|
|
return selList;
|
2000-04-10 14:29:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-04-11 22:55:29 +00:00
|
|
|
|
|
2006-10-21 00:16:43 +00:00
|
|
|
|
size_type numberOfSelections()
|
2005-11-25 09:27:08 +00:00
|
|
|
|
{
|
|
|
|
|
return theCuts.size();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2007-04-26 14:56:30 +00:00
|
|
|
|
void cutSelection(Cursor & cur, bool doclear, bool realcut)
|
2000-04-10 14:29:05 +00:00
|
|
|
|
{
|
2005-10-12 18:44:53 +00:00
|
|
|
|
// This doesn't make sense, if there is no selection
|
|
|
|
|
if (!cur.selection())
|
|
|
|
|
return;
|
2003-05-06 09:34:56 +00:00
|
|
|
|
|
2005-10-12 18:44:53 +00:00
|
|
|
|
// OK, we have a selection. This is always between cur.selBegin()
|
|
|
|
|
// and cur.selEnd()
|
2001-12-07 18:40:24 +00:00
|
|
|
|
|
2004-04-18 07:32:34 +00:00
|
|
|
|
if (cur.inTexted()) {
|
2007-04-29 23:33:02 +00:00
|
|
|
|
Text * text = cur.text();
|
2004-04-18 07:32:34 +00:00
|
|
|
|
BOOST_ASSERT(text);
|
|
|
|
|
|
|
|
|
|
// make sure that the depth behind the selection are restored, too
|
|
|
|
|
recordUndoSelection(cur);
|
2004-11-24 21:53:46 +00:00
|
|
|
|
pit_type begpit = cur.selBegin().pit();
|
|
|
|
|
pit_type endpit = cur.selEnd().pit();
|
2004-04-18 07:32:34 +00:00
|
|
|
|
|
|
|
|
|
int endpos = cur.selEnd().pos();
|
|
|
|
|
|
|
|
|
|
BufferParams const & bp = cur.buffer().params();
|
|
|
|
|
if (realcut) {
|
2006-04-20 08:42:01 +00:00
|
|
|
|
copySelectionHelper(cur.buffer(),
|
|
|
|
|
text->paragraphs(),
|
2004-04-18 07:32:34 +00:00
|
|
|
|
begpit, endpit,
|
|
|
|
|
cur.selBegin().pos(), endpos,
|
2007-02-02 03:10:15 +00:00
|
|
|
|
bp.textclass, theCuts);
|
2007-01-13 18:29:50 +00:00
|
|
|
|
// Stuff what we got on the clipboard.
|
|
|
|
|
// Even if there is no selection.
|
|
|
|
|
putClipboard(theCuts[0].first, theCuts[0].second,
|
|
|
|
|
cur.selectionAsString(true));
|
2004-04-18 07:32:34 +00:00
|
|
|
|
}
|
2004-03-25 09:16:36 +00:00
|
|
|
|
|
2004-04-20 08:51:15 +00:00
|
|
|
|
boost::tie(endpit, endpos) =
|
2004-04-18 07:32:34 +00:00
|
|
|
|
eraseSelectionHelper(bp,
|
|
|
|
|
text->paragraphs(),
|
|
|
|
|
begpit, endpit,
|
|
|
|
|
cur.selBegin().pos(), endpos,
|
|
|
|
|
doclear);
|
|
|
|
|
|
|
|
|
|
// cutSelection can invalidate the cursor so we need to set
|
|
|
|
|
// it anew. (Lgb)
|
|
|
|
|
// we prefer the end for when tracking changes
|
|
|
|
|
cur.pos() = endpos;
|
2004-11-24 21:53:46 +00:00
|
|
|
|
cur.pit() = endpit;
|
2004-04-18 07:32:34 +00:00
|
|
|
|
|
2007-02-14 11:21:39 +00:00
|
|
|
|
// sometimes necessary
|
|
|
|
|
if (doclear
|
|
|
|
|
&& text->paragraphs()[begpit].stripLeadingSpaces(bp.trackChanges))
|
|
|
|
|
cur.fixIfBroken();
|
|
|
|
|
|
2004-04-18 07:32:34 +00:00
|
|
|
|
// need a valid cursor. (Lgb)
|
|
|
|
|
cur.clearSelection();
|
2006-04-16 14:19:25 +00:00
|
|
|
|
updateLabels(cur.buffer());
|
2007-01-07 14:44:44 +00:00
|
|
|
|
theSelection().haveSelection(false);
|
2005-09-06 17:39:39 +00:00
|
|
|
|
|
|
|
|
|
// tell tabular that a recent copy happened
|
|
|
|
|
dirtyTabularStack(false);
|
2004-04-18 07:32:34 +00:00
|
|
|
|
}
|
2004-03-25 09:16:36 +00:00
|
|
|
|
|
2004-04-18 07:32:34 +00:00
|
|
|
|
if (cur.inMathed()) {
|
2005-10-12 18:44:53 +00:00
|
|
|
|
if (cur.selBegin().idx() != cur.selEnd().idx()) {
|
|
|
|
|
// The current selection spans more than one cell.
|
|
|
|
|
// Record all cells
|
|
|
|
|
recordUndoInset(cur);
|
|
|
|
|
} else {
|
|
|
|
|
// Record only the current cell to avoid a jumping
|
|
|
|
|
// cursor after undo
|
|
|
|
|
recordUndo(cur);
|
|
|
|
|
}
|
|
|
|
|
if (realcut)
|
|
|
|
|
copySelection(cur);
|
|
|
|
|
eraseSelection(cur);
|
2004-04-18 07:32:34 +00:00
|
|
|
|
}
|
2004-03-25 09:16:36 +00:00
|
|
|
|
}
|
2002-03-21 17:27:08 +00:00
|
|
|
|
|
|
|
|
|
|
2007-04-26 14:56:30 +00:00
|
|
|
|
void copySelection(Cursor & cur)
|
2004-03-25 09:16:36 +00:00
|
|
|
|
{
|
2007-01-13 18:29:50 +00:00
|
|
|
|
copySelection(cur, cur.selectionAsString(true));
|
|
|
|
|
}
|
|
|
|
|
|
2004-03-25 09:16:36 +00:00
|
|
|
|
|
2007-02-02 03:10:15 +00:00
|
|
|
|
namespace {
|
2007-01-05 14:40:49 +00:00
|
|
|
|
|
2007-04-26 14:56:30 +00:00
|
|
|
|
void copySelectionToStack(Cursor & cur, CutStack & cutstack)
|
2007-01-05 14:40:49 +00:00
|
|
|
|
{
|
2004-03-25 09:16:36 +00:00
|
|
|
|
// this doesn't make sense, if there is no selection
|
|
|
|
|
if (!cur.selection())
|
|
|
|
|
return;
|
|
|
|
|
|
2004-04-18 07:32:34 +00:00
|
|
|
|
if (cur.inTexted()) {
|
2007-04-29 23:33:02 +00:00
|
|
|
|
Text * text = cur.text();
|
2004-04-18 07:32:34 +00:00
|
|
|
|
BOOST_ASSERT(text);
|
|
|
|
|
// ok we have a selection. This is always between cur.selBegin()
|
|
|
|
|
// and sel_end cursor
|
|
|
|
|
|
|
|
|
|
// copy behind a space if there is one
|
|
|
|
|
ParagraphList & pars = text->paragraphs();
|
|
|
|
|
pos_type pos = cur.selBegin().pos();
|
2004-11-24 21:53:46 +00:00
|
|
|
|
pit_type par = cur.selBegin().pit();
|
2007-02-21 21:17:39 +00:00
|
|
|
|
while (pos < pars[par].size() &&
|
|
|
|
|
pars[par].isLineSeparator(pos) &&
|
|
|
|
|
(par != cur.selEnd().pit() || pos < cur.selEnd().pos()))
|
2004-04-18 07:32:34 +00:00
|
|
|
|
++pos;
|
|
|
|
|
|
2006-04-20 08:42:01 +00:00
|
|
|
|
copySelectionHelper(cur.buffer(), pars, par, cur.selEnd().pit(),
|
2007-02-02 03:10:15 +00:00
|
|
|
|
pos, cur.selEnd().pos(), cur.buffer().params().textclass, cutstack);
|
2004-04-18 07:32:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cur.inMathed()) {
|
2006-08-08 21:55:41 +00:00
|
|
|
|
//lyxerr << "copySelection in mathed" << endl;
|
2004-04-18 07:32:34 +00:00
|
|
|
|
ParagraphList pars;
|
2006-11-07 18:27:03 +00:00
|
|
|
|
Paragraph par;
|
2004-04-18 07:32:34 +00:00
|
|
|
|
BufferParams const & bp = cur.buffer().params();
|
2007-04-29 19:53:54 +00:00
|
|
|
|
par.layout(bp.getTextClass().defaultLayout());
|
2007-04-29 18:17:15 +00:00
|
|
|
|
par.insert(0, grabSelection(cur), Font(), Change(Change::UNCHANGED));
|
2006-11-07 18:27:03 +00:00
|
|
|
|
pars.push_back(par);
|
2007-02-02 03:10:15 +00:00
|
|
|
|
cutstack.push(make_pair(pars, bp.textclass));
|
2004-04-18 07:32:34 +00:00
|
|
|
|
}
|
2007-02-02 03:10:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void copySelectionToStack()
|
|
|
|
|
{
|
|
|
|
|
if (!selectionBuffer.empty())
|
|
|
|
|
theCuts.push(selectionBuffer[0]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2007-04-26 14:56:30 +00:00
|
|
|
|
void copySelection(Cursor & cur, docstring const & plaintext)
|
2007-02-02 03:10:15 +00:00
|
|
|
|
{
|
2007-02-22 17:55:22 +00:00
|
|
|
|
// In tablemode, because copy and paste actually use special table stack
|
|
|
|
|
// we do not attemp to get selected paragraphs under cursor. Instead, a
|
|
|
|
|
// paragraph with the plain text version is generated so that table cells
|
|
|
|
|
// can be pasted as pure text somewhere else.
|
|
|
|
|
if (cur.selBegin().idx() != cur.selEnd().idx()) {
|
|
|
|
|
ParagraphList pars;
|
|
|
|
|
Paragraph par;
|
|
|
|
|
BufferParams const & bp = cur.buffer().params();
|
2007-04-29 19:53:54 +00:00
|
|
|
|
par.layout(bp.getTextClass().defaultLayout());
|
2007-04-29 18:17:15 +00:00
|
|
|
|
par.insert(0, plaintext, Font(), Change(Change::UNCHANGED));
|
2007-02-22 17:55:22 +00:00
|
|
|
|
pars.push_back(par);
|
|
|
|
|
theCuts.push(make_pair(pars, bp.textclass));
|
|
|
|
|
} else
|
|
|
|
|
copySelectionToStack(cur, theCuts);
|
2007-02-02 03:10:15 +00:00
|
|
|
|
|
|
|
|
|
// stuff the selection onto the X clipboard, from an explicit copy request
|
|
|
|
|
putClipboard(theCuts[0].first, theCuts[0].second, plaintext);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2007-04-26 14:56:30 +00:00
|
|
|
|
void saveSelection(Cursor & cur)
|
2007-02-02 03:10:15 +00:00
|
|
|
|
{
|
2007-04-01 14:44:10 +00:00
|
|
|
|
LYXERR(Debug::ACTION) << BOOST_CURRENT_FUNCTION << ": `"
|
|
|
|
|
<< to_utf8(cur.selectionAsString(true)) << "'."
|
|
|
|
|
<< endl;
|
2007-02-02 03:10:15 +00:00
|
|
|
|
|
|
|
|
|
if (cur.selection())
|
|
|
|
|
copySelectionToStack(cur, selectionBuffer);
|
|
|
|
|
// tell X whether we now have a valid selection
|
|
|
|
|
theSelection().haveSelection(cur.selection());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool selection()
|
|
|
|
|
{
|
|
|
|
|
return !selectionBuffer.empty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void clearSelection()
|
|
|
|
|
{
|
|
|
|
|
selectionBuffer.clear();
|
2004-04-18 07:32:34 +00:00
|
|
|
|
}
|
2004-03-25 09:16:36 +00:00
|
|
|
|
|
|
|
|
|
|
2006-10-11 19:40:50 +00:00
|
|
|
|
docstring getSelection(Buffer const & buf, size_t sel_index)
|
2004-04-18 07:32:34 +00:00
|
|
|
|
{
|
|
|
|
|
return sel_index < theCuts.size()
|
|
|
|
|
? theCuts[sel_index].first.back().asString(buf, false)
|
2006-10-11 19:40:50 +00:00
|
|
|
|
: docstring();
|
2000-04-10 14:29:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-04-11 22:55:29 +00:00
|
|
|
|
|
2007-04-26 14:56:30 +00:00
|
|
|
|
void pasteParagraphList(Cursor & cur, ParagraphList const & parlist,
|
2006-08-13 16:16:43 +00:00
|
|
|
|
textclass_type textclass, ErrorList & errorList)
|
2000-04-10 14:29:05 +00:00
|
|
|
|
{
|
2005-07-16 15:22:07 +00:00
|
|
|
|
if (cur.inTexted()) {
|
2007-04-29 23:33:02 +00:00
|
|
|
|
Text * text = cur.text();
|
2005-07-16 15:22:07 +00:00
|
|
|
|
BOOST_ASSERT(text);
|
|
|
|
|
|
|
|
|
|
pit_type endpit;
|
|
|
|
|
PitPosPair ppp;
|
|
|
|
|
|
|
|
|
|
boost::tie(ppp, endpit) =
|
2006-09-19 09:39:17 +00:00
|
|
|
|
pasteSelectionHelper(cur, parlist,
|
|
|
|
|
textclass, errorList);
|
2006-04-16 14:19:25 +00:00
|
|
|
|
updateLabels(cur.buffer());
|
2005-07-16 15:22:07 +00:00
|
|
|
|
cur.clearSelection();
|
2006-12-29 23:54:48 +00:00
|
|
|
|
text->setCursor(cur.top(), ppp.first, ppp.second);
|
2005-07-16 15:22:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
2006-09-16 18:11:38 +00:00
|
|
|
|
// mathed is handled in InsetMathNest/InsetMathGrid
|
2005-10-12 18:44:53 +00:00
|
|
|
|
BOOST_ASSERT(!cur.inMathed());
|
2004-03-25 09:16:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2007-04-26 14:56:30 +00:00
|
|
|
|
void pasteFromStack(Cursor & cur, ErrorList & errorList, size_t sel_index)
|
2007-02-02 03:10:15 +00:00
|
|
|
|
{
|
|
|
|
|
// this does not make sense, if there is nothing to paste
|
|
|
|
|
if (!checkPastePossible(sel_index))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
recordUndo(cur);
|
|
|
|
|
pasteParagraphList(cur, theCuts[sel_index].first,
|
|
|
|
|
theCuts[sel_index].second, errorList);
|
|
|
|
|
cur.setSelection();
|
|
|
|
|
saveSelection(cur);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2007-04-26 14:56:30 +00:00
|
|
|
|
void pasteClipboard(Cursor & cur, ErrorList & errorList, bool asParagraphs)
|
2007-01-13 18:29:50 +00:00
|
|
|
|
{
|
|
|
|
|
// Use internal clipboard if it is the most recent one
|
|
|
|
|
if (theClipboard().isInternal()) {
|
2007-02-21 21:40:14 +00:00
|
|
|
|
pasteFromStack(cur, errorList, 0);
|
2007-01-13 18:29:50 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// First try LyX format
|
|
|
|
|
if (theClipboard().hasLyXContents()) {
|
|
|
|
|
string lyx = theClipboard().getAsLyX();
|
|
|
|
|
if (!lyx.empty()) {
|
2007-02-19 20:08:11 +00:00
|
|
|
|
// For some strange reason gcc 3.2 and 3.3 do not accept
|
|
|
|
|
// Buffer buffer(string(), false);
|
|
|
|
|
Buffer buffer("", false);
|
2007-01-13 18:29:50 +00:00
|
|
|
|
buffer.setUnnamed(true);
|
|
|
|
|
if (buffer.readString(lyx)) {
|
|
|
|
|
recordUndo(cur);
|
|
|
|
|
pasteParagraphList(cur, buffer.paragraphs(),
|
|
|
|
|
buffer.params().textclass, errorList);
|
|
|
|
|
cur.setSelection();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Then try plain text
|
|
|
|
|
docstring const text = theClipboard().getAsText();
|
|
|
|
|
if (text.empty())
|
|
|
|
|
return;
|
|
|
|
|
recordUndo(cur);
|
|
|
|
|
if (asParagraphs)
|
|
|
|
|
cur.text()->insertStringAsParagraphs(cur, text);
|
|
|
|
|
else
|
|
|
|
|
cur.text()->insertStringAsLines(cur, text);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2007-04-26 14:56:30 +00:00
|
|
|
|
void pasteSelection(Cursor & cur, ErrorList & errorList)
|
2005-11-29 15:08:35 +00:00
|
|
|
|
{
|
2007-02-02 03:10:15 +00:00
|
|
|
|
if (selectionBuffer.empty())
|
2005-11-29 15:08:35 +00:00
|
|
|
|
return;
|
2006-11-12 16:00:20 +00:00
|
|
|
|
recordUndo(cur);
|
2007-02-02 03:10:15 +00:00
|
|
|
|
pasteParagraphList(cur, selectionBuffer[0].first,
|
|
|
|
|
selectionBuffer[0].second, errorList);
|
2005-11-29 15:08:35 +00:00
|
|
|
|
cur.setSelection();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2007-04-26 14:56:30 +00:00
|
|
|
|
void replaceSelectionWithString(Cursor & cur, docstring const & str, bool backwards)
|
2004-03-25 09:16:36 +00:00
|
|
|
|
{
|
|
|
|
|
recordUndo(cur);
|
2006-08-16 21:12:20 +00:00
|
|
|
|
DocIterator selbeg = cur.selectionBegin();
|
2004-03-25 09:16:36 +00:00
|
|
|
|
|
|
|
|
|
// Get font setting before we cut
|
2007-04-29 18:17:15 +00:00
|
|
|
|
Font const font =
|
2006-08-16 21:12:20 +00:00
|
|
|
|
selbeg.paragraph().getFontSettings(cur.buffer().params(), selbeg.pos());
|
2004-03-25 09:16:36 +00:00
|
|
|
|
|
|
|
|
|
// Insert the new string
|
2006-08-16 21:12:20 +00:00
|
|
|
|
pos_type pos = cur.selEnd().pos();
|
|
|
|
|
Paragraph & par = cur.selEnd().paragraph();
|
2006-12-08 19:46:16 +00:00
|
|
|
|
docstring::const_iterator cit = str.begin();
|
|
|
|
|
docstring::const_iterator end = str.end();
|
2004-03-25 09:16:36 +00:00
|
|
|
|
for (; cit != end; ++cit, ++pos)
|
2006-12-08 19:46:16 +00:00
|
|
|
|
par.insertChar(pos, *cit, font, cur.buffer().params().trackChanges);
|
2004-03-25 09:16:36 +00:00
|
|
|
|
|
|
|
|
|
// Cut the selection
|
|
|
|
|
cutSelection(cur, true, false);
|
2006-08-16 21:12:20 +00:00
|
|
|
|
|
|
|
|
|
// select the replacement
|
|
|
|
|
if (backwards) {
|
|
|
|
|
selbeg.pos() += str.length();
|
2006-10-21 11:29:34 +00:00
|
|
|
|
cur.setSelection(selbeg, -int(str.length()));
|
2006-08-16 21:12:20 +00:00
|
|
|
|
} else
|
|
|
|
|
cur.setSelection(selbeg, str.length());
|
2007-02-02 03:10:15 +00:00
|
|
|
|
saveSelection(cur);
|
2004-03-25 09:16:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2007-04-26 14:56:30 +00:00
|
|
|
|
void replaceSelection(Cursor & cur)
|
2004-03-25 09:16:36 +00:00
|
|
|
|
{
|
|
|
|
|
if (cur.selection())
|
|
|
|
|
cutSelection(cur, true, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2007-04-26 14:56:30 +00:00
|
|
|
|
void eraseSelection(Cursor & cur)
|
2004-04-18 07:32:34 +00:00
|
|
|
|
{
|
2007-01-04 16:50:03 +00:00
|
|
|
|
//lyxerr << "cap::eraseSelection begin: " << cur << endl;
|
2004-04-18 07:32:34 +00:00
|
|
|
|
CursorSlice const & i1 = cur.selBegin();
|
|
|
|
|
CursorSlice const & i2 = cur.selEnd();
|
2006-09-16 18:11:38 +00:00
|
|
|
|
if (i1.inset().asInsetMath()) {
|
2005-04-06 07:38:24 +00:00
|
|
|
|
cur.top() = i1;
|
2004-04-18 07:32:34 +00:00
|
|
|
|
if (i1.idx() == i2.idx()) {
|
|
|
|
|
i1.cell().erase(i1.pos(), i2.pos());
|
2005-10-12 18:44:53 +00:00
|
|
|
|
// We may have deleted i1.cell(cur.pos()).
|
|
|
|
|
// Make sure that pos is valid.
|
|
|
|
|
if (cur.pos() > cur.lastpos())
|
|
|
|
|
cur.pos() = cur.lastpos();
|
2004-04-18 07:32:34 +00:00
|
|
|
|
} else {
|
2006-09-16 18:11:38 +00:00
|
|
|
|
InsetMath * p = i1.asInsetMath();
|
2007-04-29 13:39:47 +00:00
|
|
|
|
Inset::row_type r1, r2;
|
|
|
|
|
Inset::col_type c1, c2;
|
2004-04-18 07:32:34 +00:00
|
|
|
|
region(i1, i2, r1, r2, c1, c2);
|
2007-04-29 13:39:47 +00:00
|
|
|
|
for (Inset::row_type row = r1; row <= r2; ++row)
|
|
|
|
|
for (Inset::col_type col = c1; col <= c2; ++col)
|
2004-04-18 07:32:34 +00:00
|
|
|
|
p->cell(p->index(row, col)).clear();
|
2005-04-06 07:38:24 +00:00
|
|
|
|
// We've deleted the whole cell. Only pos 0 is valid.
|
2005-04-26 11:12:20 +00:00
|
|
|
|
cur.pos() = 0;
|
2004-04-18 07:32:34 +00:00
|
|
|
|
}
|
2005-10-12 18:44:53 +00:00
|
|
|
|
// need a valid cursor. (Lgb)
|
|
|
|
|
cur.clearSelection();
|
2007-01-07 14:44:44 +00:00
|
|
|
|
theSelection().haveSelection(false);
|
2004-04-18 07:32:34 +00:00
|
|
|
|
} else {
|
|
|
|
|
lyxerr << "can't erase this selection 1" << endl;
|
|
|
|
|
}
|
2007-01-04 16:50:03 +00:00
|
|
|
|
//lyxerr << "cap::eraseSelection end: " << cur << endl;
|
2004-04-18 07:32:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2007-04-26 14:56:30 +00:00
|
|
|
|
void selDel(Cursor & cur)
|
2004-04-18 07:32:34 +00:00
|
|
|
|
{
|
2007-01-04 16:50:03 +00:00
|
|
|
|
//lyxerr << "cap::selDel" << endl;
|
2005-10-12 18:44:53 +00:00
|
|
|
|
if (cur.selection())
|
2004-04-18 07:32:34 +00:00
|
|
|
|
eraseSelection(cur);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2007-04-26 14:56:30 +00:00
|
|
|
|
void selClearOrDel(Cursor & cur)
|
2004-04-18 07:32:34 +00:00
|
|
|
|
{
|
2007-01-04 16:50:03 +00:00
|
|
|
|
//lyxerr << "cap::selClearOrDel" << endl;
|
2004-04-18 07:32:34 +00:00
|
|
|
|
if (lyxrc.auto_region_delete)
|
|
|
|
|
selDel(cur);
|
|
|
|
|
else
|
|
|
|
|
cur.selection() = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2007-04-26 14:56:30 +00:00
|
|
|
|
docstring grabSelection(Cursor const & cur)
|
2004-04-18 07:32:34 +00:00
|
|
|
|
{
|
|
|
|
|
if (!cur.selection())
|
2006-10-22 10:15:23 +00:00
|
|
|
|
return docstring();
|
2004-04-18 07:32:34 +00:00
|
|
|
|
|
2005-10-12 18:44:53 +00:00
|
|
|
|
// FIXME: What is wrong with the following?
|
|
|
|
|
#if 0
|
|
|
|
|
std::ostringstream os;
|
|
|
|
|
for (DocIterator dit = cur.selectionBegin();
|
|
|
|
|
dit != cur.selectionEnd(); dit.forwardPos())
|
|
|
|
|
os << asString(dit.cell());
|
|
|
|
|
return os.str();
|
|
|
|
|
#endif
|
|
|
|
|
|
2004-04-18 07:32:34 +00:00
|
|
|
|
CursorSlice i1 = cur.selBegin();
|
|
|
|
|
CursorSlice i2 = cur.selEnd();
|
|
|
|
|
|
|
|
|
|
if (i1.idx() == i2.idx()) {
|
2006-09-16 18:11:38 +00:00
|
|
|
|
if (i1.inset().asInsetMath()) {
|
2007-04-26 16:06:39 +00:00
|
|
|
|
MathData::const_iterator it = i1.cell().begin();
|
|
|
|
|
return asString(MathData(it + i1.pos(), it + i2.pos()));
|
2004-04-18 07:32:34 +00:00
|
|
|
|
} else {
|
2006-10-22 10:15:23 +00:00
|
|
|
|
return from_ascii("unknown selection 1");
|
2004-04-18 07:32:34 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2007-04-29 13:39:47 +00:00
|
|
|
|
Inset::row_type r1, r2;
|
|
|
|
|
Inset::col_type c1, c2;
|
2004-04-18 07:32:34 +00:00
|
|
|
|
region(i1, i2, r1, r2, c1, c2);
|
|
|
|
|
|
2006-10-22 10:15:23 +00:00
|
|
|
|
docstring data;
|
2006-09-16 18:11:38 +00:00
|
|
|
|
if (i1.inset().asInsetMath()) {
|
2007-04-29 13:39:47 +00:00
|
|
|
|
for (Inset::row_type row = r1; row <= r2; ++row) {
|
2004-04-18 07:32:34 +00:00
|
|
|
|
if (row > r1)
|
|
|
|
|
data += "\\\\";
|
2007-04-29 13:39:47 +00:00
|
|
|
|
for (Inset::col_type col = c1; col <= c2; ++col) {
|
2004-04-18 07:32:34 +00:00
|
|
|
|
if (col > c1)
|
|
|
|
|
data += '&';
|
2006-09-16 18:11:38 +00:00
|
|
|
|
data += asString(i1.asInsetMath()->
|
|
|
|
|
cell(i1.asInsetMath()->index(row, col)));
|
2004-04-18 07:32:34 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2006-10-22 10:15:23 +00:00
|
|
|
|
data = from_ascii("unknown selection 2");
|
2004-04-18 07:32:34 +00:00
|
|
|
|
}
|
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2005-09-06 17:39:39 +00:00
|
|
|
|
void dirtyTabularStack(bool b)
|
|
|
|
|
{
|
|
|
|
|
dirty_tabular_stack_ = b;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool tabularStackDirty()
|
|
|
|
|
{
|
|
|
|
|
return dirty_tabular_stack_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2004-03-25 09:16:36 +00:00
|
|
|
|
} // namespace cap
|
2006-10-21 00:16:43 +00:00
|
|
|
|
|
2004-03-25 09:16:36 +00:00
|
|
|
|
} // namespace lyx
|