Am Freitag, 12. Januar 2007 01:10 schrieb Bennett Helm:

> LyXFunc::dispatch: cmd:  action: 15 arg: 'paragraph' x: 0 y: 0
> LyXFunc::dispatch: primary-selection-paste [15] is disabled at this  

This is of course wrong. I used LFUN_PRIMARY_SELECTION_PASTE instead of 
LFUN_CLIPBOARD_PASTE in pasteClipboard(). I did not notice that because I 
always pasted when the selection in the other program was still available, 
so LFUN_PRIMARY_SELECTION_PASTE did work for me.
I cannot use LFUN_CLIPBOARD_PASTE (infinite recursion), but this patch 
should now do exactly the same as current trunk if the external clipboard 
does not contain LyX formatted stuff.
Bennet, could you test this once again please? I only need a console log if 
this still does not work, but I am pretty sure that it works now.


Georg
Index: src/insets/insettabular.C
===================================================================
--- src/insets/insettabular.C	(Revision 16664)
+++ src/insets/insettabular.C	(Arbeitskopie)
@@ -723,7 +723,7 @@ void InsetTabular::doDispatch(LCursor & 
 	case LFUN_CLIPBOARD_PASTE:
 	case LFUN_PRIMARY_SELECTION_PASTE: {
 		docstring const clip = (cmd.action == LFUN_CLIPBOARD_PASTE) ?
-			theClipboard().get() :
+			theClipboard().getAsText() :
 			theSelection().get();
 		if (clip.empty())
 			break;
@@ -1814,10 +1814,13 @@ bool InsetTabular::copySelection(LCursor
 	odocstringstream os;
 	OutputParams const runparams;
 	paste_tabular->plaintext(cur.buffer(), os, runparams, 0, true, '\t');
-	theClipboard().put(os.str());
+	// Needed for the "Edit->Paste recent" menu and the system clipboard.
+	cap::copySelection(cur, os.str());
+
 	// mark tabular stack dirty
 	// FIXME: this is a workaround for bug 1919. Should be removed for 1.5,
 	// when we (hopefully) have a one-for-all paste mechanism.
+	// This must be called after cap::copySelection.
 	dirtyTabularStack(true);
 
 	return true;
Index: src/mathed/InsetMathGrid.C
===================================================================
--- src/mathed/InsetMathGrid.C	(Revision 16664)
+++ src/mathed/InsetMathGrid.C	(Arbeitskopie)
@@ -1213,7 +1213,7 @@ void InsetMathGrid::doDispatch(LCursor &
 		cap::replaceSelection(cur);
 		docstring topaste;
 		if (cmd.argument().empty() && !theClipboard().isInternal())
-			topaste = theClipboard().get();
+			topaste = theClipboard().getAsText();
 		else {
 			idocstringstream is(cmd.argument());
 			int n = 0;
Index: src/mathed/InsetMathNest.C
===================================================================
--- src/mathed/InsetMathNest.C	(Revision 16664)
+++ src/mathed/InsetMathNest.C	(Arbeitskopie)
@@ -440,7 +440,7 @@ void InsetMathNest::doDispatch(LCursor &
 		replaceSelection(cur);
 		docstring topaste;
 		if (cmd.argument().empty() && !theClipboard().isInternal())
-			topaste = theClipboard().get();
+			topaste = theClipboard().getAsText();
 		else {
 			size_t n = 0;
 			idocstringstream is(cmd.argument());
Index: src/buffer.C
===================================================================
--- src/buffer.C	(Revision 16664)
+++ src/buffer.C	(Arbeitskopie)
@@ -566,6 +566,39 @@ void Buffer::insertStringAsLines(Paragra
 }
 
 
+bool Buffer::readString(std::string const & s)
+{
+	params().compressed = false;
+
+	// remove dummy empty par
+	paragraphs().clear();
+	LyXLex lex(0, 0);
+	std::istringstream is(s);
+	lex.setStream(is);
+	FileName const name(tempName());
+	switch (readFile(lex, name)) {
+	case failure:
+		return false;
+	case wrongversion: {
+		// We need to call lyx2lyx, so write the input to a file
+		std::ofstream os(name.toFilesystemEncoding().c_str());
+		os << s;
+		os.close();
+		return readFile(name) == success;
+	}
+	case success:
+		break;
+	}
+
+	// After we have read a file, we must ensure that the buffer
+	// language is set and used in the gui.
+	// If you know of a better place to put this, please tell me. (Lgb)
+	updateDocLang(params().language);
+
+	return true;
+}
+
+
 bool Buffer::readFile(FileName const & filename)
 {
 	// Check if the file is compressed.
@@ -578,7 +611,7 @@ bool Buffer::readFile(FileName const & f
 	paragraphs().clear();
 	LyXLex lex(0, 0);
 	lex.setFile(filename);
-	if (!readFile(lex, filename))
+	if (readFile(lex, filename) != success)
 		return false;
 
 	// After we have read a file, we must ensure that the buffer
@@ -602,14 +635,15 @@ void Buffer::fully_loaded(bool const val
 }
 
 
-bool Buffer::readFile(LyXLex & lex, FileName const & filename)
+Buffer::ReadStatus Buffer::readFile(LyXLex & lex, FileName const & filename,
+		bool fromstring)
 {
 	BOOST_ASSERT(!filename.empty());
 
 	if (!lex.isOK()) {
 		Alert::error(_("Document could not be read"),
 			     bformat(_("%1$s could not be read."), from_utf8(filename.absFilename())));
-		return false;
+		return failure;
 	}
 
 	lex.next();
@@ -618,7 +652,7 @@ bool Buffer::readFile(LyXLex & lex, File
 	if (!lex.isOK()) {
 		Alert::error(_("Document could not be read"),
 			     bformat(_("%1$s could not be read."), from_utf8(filename.absFilename())));
-		return false;
+		return failure;
 	}
 
 	// the first token _must_ be...
@@ -628,7 +662,7 @@ bool Buffer::readFile(LyXLex & lex, File
 		Alert::error(_("Document format failure"),
 			     bformat(_("%1$s is not a LyX document."),
 				       from_utf8(filename.absFilename())));
-		return false;
+		return failure;
 	}
 
 	lex.next();
@@ -643,6 +677,11 @@ bool Buffer::readFile(LyXLex & lex, File
 	//lyxerr << "format: " << file_format << endl;
 
 	if (file_format != LYX_FORMAT) {
+
+		if (fromstring)
+			// lyx2lyx would fail
+			return wrongversion;
+
 		FileName const tmpfile(tempName());
 		if (tmpfile.empty()) {
 			Alert::error(_("Conversion failed"),
@@ -651,7 +690,7 @@ bool Buffer::readFile(LyXLex & lex, File
 					      " file for converting it could"
 							    " not be created."),
 					      from_utf8(filename.absFilename())));
-			return false;
+			return failure;
 		}
 		FileName const lyx2lyx = libFileSearch("lyx2lyx", "lyx2lyx");
 		if (lyx2lyx.empty()) {
@@ -661,7 +700,7 @@ bool Buffer::readFile(LyXLex & lex, File
 					       " conversion script lyx2lyx"
 							    " could not be found."),
 					       from_utf8(filename.absFilename())));
-			return false;
+			return failure;
 		}
 		ostringstream command;
 		command << os::python()
@@ -682,11 +721,11 @@ bool Buffer::readFile(LyXLex & lex, File
 					      " of LyX, but the lyx2lyx script"
 							    " failed to convert it."),
 					      from_utf8(filename.absFilename())));
-			return false;
+			return failure;
 		} else {
 			bool const ret = readFile(tmpfile);
 			// Do stuff with tmpfile name and buffer name here.
-			return ret;
+			return ret ? success : failure;
 		}
 
 	}
@@ -703,7 +742,7 @@ bool Buffer::readFile(LyXLex & lex, File
 	//MacroTable::localMacros().clear();
 
 	pimpl_->file_fully_loaded = true;
-	return true;
+	return success;
 }
 
 
@@ -763,20 +802,20 @@ bool Buffer::writeFile(FileName const & 
 		if (!ofs)
 			return false;
 
-		retval = do_writeFile(ofs);
+		retval = write(ofs);
 	} else {
 		ofstream ofs(fname.toFilesystemEncoding().c_str(), ios::out|ios::trunc);
 		if (!ofs)
 			return false;
 
-		retval = do_writeFile(ofs);
+		retval = write(ofs);
 	}
 
 	return retval;
 }
 
 
-bool Buffer::do_writeFile(ostream & ofs) const
+bool Buffer::write(ostream & ofs) const
 {
 #ifdef HAVE_LOCALE
 	// Use the standard "C" locale for file output.
Index: src/CutAndPaste.C
===================================================================
--- src/CutAndPaste.C	(Revision 16664)
+++ src/CutAndPaste.C	(Arbeitskopie)
@@ -26,6 +26,7 @@
 #include "insetiterator.h"
 #include "language.h"
 #include "lfuns.h"
+#include "lyxfunc.h"
 #include "lyxrc.h"
 #include "lyxtext.h"
 #include "lyxtextclasslist.h"
@@ -324,6 +325,21 @@ PitPosPair eraseSelectionHelper(BufferPa
 }
 
 
+void putClipboard(ParagraphList const & paragraphs, textclass_type textclass,
+                  docstring const & plaintext)
+{
+	Buffer buffer(string(), false);
+	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);
+}
+
+
 void copySelectionHelper(Buffer const & buf, ParagraphList & pars,
 	pit_type startpit, pit_type endpit,
 	int start, int end, textclass_type tc)
@@ -493,9 +509,6 @@ void cutSelection(LCursor & cur, bool do
 	if (cur.inTexted()) {
 		LyXText * text = cur.text();
 		BOOST_ASSERT(text);
-		// Stuff what we got on the clipboard. Even if there is no selection.
-		if (realcut)
-			theClipboard().put(cur.selectionAsString(true));
 
 		// make sure that the depth behind the selection are restored, too
 		recordUndoSelection(cur);
@@ -511,6 +524,10 @@ void cutSelection(LCursor & cur, bool do
 				begpit, endpit,
 				cur.selBegin().pos(), endpos,
 				bp.textclass);
+			// Stuff what we got on the clipboard.
+			// Even if there is no selection.
+			putClipboard(theCuts[0].first, theCuts[0].second,
+				cur.selectionAsString(true));
 		}
 
 		boost::tie(endpit, endpos) =
@@ -558,10 +575,16 @@ void cutSelection(LCursor & cur, bool do
 
 void copySelection(LCursor & cur)
 {
-	// stuff the selection onto the X clipboard, from an explicit copy request
-	theClipboard().put(cur.selectionAsString(true));
+	copySelection(cur, cur.selectionAsString(true));
+}
+
 
+void copySelection(LCursor & cur, docstring const & plaintext)
+{
 	copySelectionToStack(cur);
+
+	// stuff the selection onto the X clipboard, from an explicit copy request
+	putClipboard(theCuts[0].first, theCuts[0].second, plaintext);
 }
 
 
@@ -636,6 +659,42 @@ void pasteParagraphList(LCursor & cur, P
 }
 
 
+void pasteClipboard(LCursor & cur, ErrorList & errorList, bool asParagraphs)
+{
+	// Use internal clipboard if it is the most recent one
+	if (theClipboard().isInternal()) {
+		pasteSelection(cur, errorList, 0);
+		return;
+	}
+
+	// First try LyX format
+	if (theClipboard().hasLyXContents()) {
+		string lyx = theClipboard().getAsLyX();
+		if (!lyx.empty()) {
+			Buffer buffer(string(), false);
+			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);
+}
+
+
 void pasteSelection(LCursor & cur, ErrorList & errorList, size_t sel_index)
 {
 	// this does not make sense, if there is nothing to paste
Index: src/buffer.h
===================================================================
--- src/buffer.h	(Revision 16664)
+++ src/buffer.h	(Arbeitskopie)
@@ -78,6 +78,13 @@ public:
 		buildlog  ///< Literate build log
 	};
 
+	/// Result of \c readFile()
+	enum ReadStatus {
+		failure, ///< The file could not be read
+		success, ///< The file could not be read
+		wrongversion ///< The version of the file does not match ours
+	};
+
 	/** Constructor
 	    \param file
 	    \param b  optional \c false by default
@@ -98,6 +105,8 @@ public:
 	/// Load the autosaved file.
 	void loadAutoSaveFile();
 
+	/// read a new document from a string
+	bool readString(std::string const &);
 	/// load a new file
 	bool readFile(support::FileName const & filename);
 
@@ -143,6 +152,8 @@ public:
 	*/
 	bool save() const;
 
+	/// Write document to stream. Returns \c false if unsuccesful.
+	bool write(std::ostream &) const;
 	/// Write file. Returns \c false if unsuccesful.
 	bool writeFile(support::FileName const &) const;
 
@@ -385,9 +396,8 @@ private:
 	/** Inserts a file into a document
 	    \return \c false if method fails.
 	*/
-	bool readFile(LyXLex &, support::FileName const & filename);
-
-	bool do_writeFile(std::ostream & ofs) const;
+	ReadStatus readFile(LyXLex &, support::FileName const & filename,
+	                    bool fromString = false);
 
 	/// Use the Pimpl idiom to hide the internals.
 	class Impl;
Index: src/CutAndPaste.h
===================================================================
--- src/CutAndPaste.h	(Revision 16664)
+++ src/CutAndPaste.h	(Arbeitskopie)
@@ -62,9 +62,19 @@ void replaceSelection(LCursor & cur);
 void cutSelection(LCursor & cur, bool doclear = true, bool realcut = true);
 /// Push the current selection to the cut buffer and the system clipboard.
 void copySelection(LCursor & cur);
+/**
+ * Push the current selection to the cut buffer and the system clipboard.
+ * \param plaintext plain text version of the selection for the system
+ *        clipboard
+ */
+void copySelection(LCursor & cur, docstring const & plaintext);
 /// Push the current selection to the cut buffer.
 void copySelectionToStack(LCursor & cur);
-/// Paste the sel_index-th element of the cut buffer.
+/// Replace the current selection with the clipboard contents (internal or
+/// external: which is newer)
+/// Does handle undo. Does only work in text, not mathed.
+void pasteClipboard(LCursor & cur, ErrorList & errorList, bool asParagraphs = true);
+/// Replace the current selection with cut buffer \c sel_index
 /// Does handle undo. Does only work in text, not mathed.
 void pasteSelection(LCursor & cur, ErrorList &, size_t sel_index = 0);
 
Index: src/frontends/Clipboard.h
===================================================================
--- src/frontends/Clipboard.h	(Revision 16664)
+++ src/frontends/Clipboard.h	(Arbeitskopie)
@@ -28,18 +28,29 @@ public:
 	virtual ~Clipboard() {}
 
 	/**
-	 * Get the window system clipboard contents.
+	 * Get the system clipboard contents. The format is as written in
+	 * .lyx files (may even be an older version than ours if it comes
+	 * from an older LyX).
+	 * Does not convert plain text to LyX if only plain text is available.
 	 * This should be called when the user requests to paste from the
 	 * clipboard.
 	 */
-	virtual docstring const get() const = 0;
+	virtual std::string const getAsLyX() const = 0;
+	/// Get the contents of the window system clipboard in plain text format.
+	virtual docstring const getAsText() const = 0;
 	/**
-	 * Fill the window system clipboard.
+	 * Fill the system clipboard. The format of \p lyx is as written in
+	 * .lyx files, the format of \p text is plain text.
+	 * We put the clipboard contents in LyX format and plain text into
+	 * the system clipboard if supported, so that it is useful for other
+	 * applications as well as other instances of LyX.
 	 * This should be called when the user requests to cut or copy to
 	 * the clipboard.
 	 */
-	virtual void put(docstring const &) = 0;
+	virtual void put(std::string const & lyx, docstring const & text) = 0;
 
+	/// Does the clipboard contain LyX contents?
+	virtual bool hasLyXContents() const = 0;
 	/// state of clipboard.
 	/// \retval true if the system clipboard has been set within LyX.
 	virtual bool isInternal() const = 0;
Index: src/frontends/qt4/GuiClipboard.C
===================================================================
--- src/frontends/qt4/GuiClipboard.C	(Revision 16664)
+++ src/frontends/qt4/GuiClipboard.C	(Arbeitskopie)
@@ -19,6 +19,7 @@
 
 #include <QApplication>
 #include <QClipboard>
+#include <QMimeData>
 #include <QString>
 
 #include "support/lstrings.h"
@@ -26,15 +27,50 @@ using lyx::support::internalLineEnding;
 using lyx::support::externalLineEnding;
 
 using std::endl;
+using std::string;
+
+
+namespace {
+
+char const * const mime_type = "application/x-lyx";
+
+}
+
 
 namespace lyx {
 namespace frontend {
 
-docstring const GuiClipboard::get() const
+string const GuiClipboard::getAsLyX() const
 {
+	lyxerr[Debug::ACTION] << "GuiClipboard::getAsLyX(): `";
+	// We don't convert encodings here since the encoding of the
+	// clipboard contents is specified in the data itself
+	QMimeData const * source =
+		qApp->clipboard()->mimeData(QClipboard::Clipboard);
+	if (!source) {
+		lyxerr[Debug::ACTION] << "' (no QMimeData)" << endl;
+		return string();
+	}
+	if (source->hasFormat(mime_type)) {
+		// data from ourself or some other LyX instance
+		QByteArray const ar = source->data(mime_type);
+		string const s(ar.data(), ar.count());
+		if (lyxerr.debugging(Debug::ACTION))
+			lyxerr[Debug::ACTION] << s << "'" << endl;
+		return s;
+	}
+	lyxerr[Debug::ACTION] << "'" << endl;
+	return string();
+}
+
+
+docstring const GuiClipboard::getAsText() const
+{
+	// text data from other applications
 	QString const str = qApp->clipboard()->text(QClipboard::Clipboard);
-	lyxerr[Debug::ACTION] << "GuiClipboard::get: " << fromqstr(str)
-	                      << endl;
+	if (lyxerr.debugging(Debug::ACTION))
+		lyxerr[Debug::ACTION] << "GuiClipboard::getAsText(): `"
+		                      << fromqstr(str) << "'" << endl;
 	if (str.isNull())
 		return docstring();
 
@@ -42,24 +78,49 @@ docstring const GuiClipboard::get() cons
 }
 
 
-void GuiClipboard::put(docstring const & str)
+void GuiClipboard::put(string const & lyx, docstring const & text)
 {
-	lyxerr[Debug::ACTION] << "GuiClipboard::put: " << lyx::to_utf8(str) << endl;
+	if (lyxerr.debugging(Debug::ACTION))
+		lyxerr[Debug::ACTION] << "GuiClipboard::put(`" << lyx << "' `"
+		                      << to_utf8(text) << "')" << endl;
+	// We don't convert the encoding of lyx since the encoding of the
+	// clipboard contents is specified in the data itself
+	QMimeData * data = new QMimeData;
+	if (!lyx.empty()) {
+		QByteArray const qlyx(lyx.c_str(), lyx.size());
+		data->setData(mime_type, qlyx);
+	}
+	// Don't test for text.empty() since we want to be able to clear the
+	// clipboard.
+	QString const qtext = toqstr(text);
+	data->setText(qtext);
+	qApp->clipboard()->setMimeData(data, QClipboard::Clipboard);
+}
+
 
-	qApp->clipboard()->setText(toqstr(externalLineEnding(str)),
-	                           QClipboard::Clipboard);
+bool GuiClipboard::hasLyXContents() const
+{
+	QMimeData const * const source =
+		qApp->clipboard()->mimeData(QClipboard::Clipboard);
+	bool const retval = source && source->hasFormat(mime_type);
+	lyxerr[Debug::ACTION] << "GuiClipboard::hasLyXContents(): " << retval << endl;
+	return retval;
 }
 
 
 bool GuiClipboard::isInternal() const
 {
-	return qApp->clipboard()->ownsClipboard();
+	bool const retval = qApp->clipboard()->ownsClipboard();
+	lyxerr[Debug::ACTION] << "GuiClipboard::isInternal(): " << retval << endl;
+	return retval;
 }
 
 
 bool GuiClipboard::empty() const
 {
-	return qApp->clipboard()->text(QClipboard::Clipboard).isEmpty();
+	bool const retval = qApp->clipboard()->text(QClipboard::Clipboard).isEmpty();
+	lyxerr[Debug::ACTION] << "GuiClipboard::empty(): " << retval << endl;
+	return retval;
 }
 
 } // namespace frontend
Index: src/frontends/qt4/GuiClipboard.h
===================================================================
--- src/frontends/qt4/GuiClipboard.h	(Revision 16664)
+++ src/frontends/qt4/GuiClipboard.h	(Arbeitskopie)
@@ -30,8 +30,10 @@ public:
 	/** Clipboard overloaded methods
 	 */
 	//@{
-	docstring const get() const;
-	void put(docstring const & str);
+	std::string const getAsLyX() const;
+	docstring const getAsText() const;
+	void put(std::string const & lyx, docstring const & text);
+	bool hasLyXContents() const;
 	bool isInternal() const;
 	bool empty() const;
 	//@}
Index: src/text3.C
===================================================================
--- src/text3.C	(Revision 16664)
+++ src/text3.C	(Arbeitskopie)
@@ -76,6 +76,7 @@ namespace lyx {
 
 using cap::copySelection;
 using cap::cutSelection;
+using cap::pasteClipboard;
 using cap::pasteSelection;
 using cap::replaceSelection;
 
@@ -758,15 +759,15 @@ void LyXText::dispatch(LCursor & cur, Fu
 		cur.message(_("Paste"));
 		cap::replaceSelection(cur);
 		if (cmd.argument().empty() && !theClipboard().isInternal())
-			pasteString(cur, theClipboard().get(), true);
+			pasteClipboard(cur, bv->buffer()->errorList("Paste"));
 		else {
 			string const arg(to_utf8(cmd.argument()));
 			pasteSelection(cur, bv->buffer()->errorList("Paste"),
 					isStrUnsignedInt(arg) ?
 						convert<unsigned int>(arg) :
 						0);
-			bv->buffer()->errors("Paste");
 		}
+		bv->buffer()->errors("Paste");
 		cur.clearSelection(); // bug 393
 		bv->switchKeyMap();
 		finishUndo();
@@ -865,8 +866,10 @@ void LyXText::dispatch(LCursor & cur, Fu
 	}
 
 	case LFUN_CLIPBOARD_PASTE:
-		pasteString(cur, theClipboard().get(),
-		            cmd.argument() == "paragraph");
+		cur.clearSelection();
+		pasteClipboard(cur, bv->buffer()->errorList("Paste"),
+		               cmd.argument() == "paragraph");
+		bv->buffer()->errors("Paste");
 		break;
 
 	case LFUN_PRIMARY_SELECTION_PASTE:

Reply via email to