LyX instances. This re-enables copy/paste from the internal clipboard on
OS X (currently broken since Clipboard::isInternal() always returns false for
some reason).
* src/insets/insettabular.C
(InsetTabular::doDispatch): adjust to clipboard interface change
(InsetTabular::copySelection): ditto
* src/mathed/InsetMathGrid.C
(InsetMathGrid::doDispatch): ditto
* src/mathed/InsetMathNest.C
(InsetMathNest::doDispatch): ditto
* src/buffer.[Ch]
(Buffer::readString): New method: Read document from a string
(Buffer::readFile): Change return value from bool to enum (needed
for readString). Return wrongversion if we are reading from a string
and the version does not match.
(Buffer::do_writeFile): make public and rename to write
* src/CutAndPaste.C
(putClipboard): New helper, put stuff to the system clipboard
(void copySelectionHelper): Use putClipboard instead of
theClipboard().put()
(void copySelection): ditto
(void pasteClipboard): new method for pasting in text
(void pasteParagraphList):
* src/frontends/Clipboard.h
(Clipboard::get): Rename to getAsText
(Clipboard::getAsLyX): New method for getting the system clipboard
in LyX format
(Clipboard::hasLyXContents): New method telling whether there is LyX
contents in the clipboard
* src/frontends/qt4/GuiClipboard.[Ch]: Implement the new methods
* src/text3.C
(LyXText::dispatch): Use pasteClipboard for pasting the system
clipboard
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@16669
a592a061-630c-0410-9148-
cb99ea01b6c8
#include "insetiterator.h"
#include "language.h"
#include "lfuns.h"
+#include "lyxfunc.h"
#include "lyxrc.h"
#include "lyxtext.h"
#include "lyxtextclasslist.h"
}
+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)
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);
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) =
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);
}
}
+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
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);
}
+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.
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
}
-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();
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...
Alert::error(_("Document format failure"),
bformat(_("%1$s is not a LyX document."),
from_utf8(filename.absFilename())));
- return false;
+ return failure;
}
lex.next();
//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"),
" 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()) {
" conversion script lyx2lyx"
" could not be found."),
from_utf8(filename.absFilename())));
- return false;
+ return failure;
}
ostringstream command;
command << os::python()
" 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;
}
}
//MacroTable::localMacros().clear();
pimpl_->file_fully_loaded = true;
- return true;
+ return success;
}
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.
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
/// 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);
*/
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;
/** 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;
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;
#include <QApplication>
#include <QClipboard>
+#include <QMimeData>
#include <QString>
#include "support/lstrings.h"
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();
}
-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);
+ return source && source->hasFormat(mime_type);
}
/** 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;
//@}
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;
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;
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;
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());
using cap::copySelection;
using cap::cutSelection;
+using cap::pasteClipboard;
using cap::pasteSelection;
using cap::replaceSelection;
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();
}
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: