Fix #6597: LyX Appears frozen if the process holding the clipboard is frozen

Implements CacheMimeData type so that we only need to query the
clipboard once on startup and once each time the contents of the
clipboard change. This is important as Qt takes 5 seconds to time-out when the clipboard is non-responsive.

Patch by John McCabe-Dansted.

git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@35790 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
Vincent van Ravesteijn 2010-10-23 00:21:58 +00:00
parent ad51a8e88e
commit f3ed8071d8
2 changed files with 88 additions and 34 deletions

View File

@ -27,6 +27,7 @@
#include "support/filetools.h"
#include "support/gettext.h"
#include "support/lstrings.h"
#include "support/lyxtime.h"
#ifdef Q_WS_MACX
#include "support/linkback/LinkBackProxy.h"
@ -55,6 +56,43 @@ namespace lyx {
namespace frontend {
static QMimeData const * read_clipboard()
{
LYXERR(Debug::ACTION, "Getting Clipboard");
QMimeData const * source =
qApp->clipboard()->mimeData(QClipboard::Clipboard);
if (!source) {
LYXERR0("0 bytes (no QMimeData)");
return new QMimeData();
}
// It appears that doing IO between getting a mimeData object
// and using it can cause a crash (maybe Qt used IO
// as an excuse to free() it? Anyway let's not introduce
// any new IO here, so e.g. leave the following line commented.
// lyxerr << "Got Clipboard (" << (long) source << ")\n" ;
return source;
}
void CacheMimeData::update()
{
time_t const start_time = current_time();
LYXERR(Debug::ACTION, "Creating CacheMimeData object");
cached_formats_ = read_clipboard()->formats();
// Qt times out after 5 seconds if it does not recieve a response.
if (current_time() - start_time > 3) {
lyxerr << "No timely response from clipboard, perhaps process "
<< "holding clipboard is frozen?" << endl;
}
}
QByteArray CacheMimeData::data(QString const & mimeType) const
{
return read_clipboard()->data(mimeType);
}
QString const lyxMimeType(){ return "application/x-lyx"; }
QString const pdfMimeType(){ return "application/pdf"; }
@ -76,16 +114,9 @@ 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)");
return string();
}
if (source->hasFormat(lyxMimeType())) {
if (cache_.hasFormat(lyxMimeType())) {
// data from ourself or some other LyX instance
QByteArray const ar = source->data(lyxMimeType());
QByteArray const ar = cache_.data(lyxMimeType());
string const s(ar.data(), ar.count());
LYXERR(Debug::ACTION, s << "'");
return s;
@ -247,14 +278,6 @@ FileName GuiClipboard::getAsGraphics(Cursor const & cur, GraphicsType type) cons
return filename;
}
// get mime data
QMimeData const * source =
qApp->clipboard()->mimeData(QClipboard::Clipboard);
if (!source) {
LYXERR(Debug::ACTION, "0 bytes (no QMimeData)");
return FileName();
}
// get mime for type
QString mime;
switch (type) {
@ -266,10 +289,10 @@ FileName GuiClipboard::getAsGraphics(Cursor const & cur, GraphicsType type) cons
}
// get data
if (!source->hasFormat(mime))
if (!cache_.hasFormat(mime))
return FileName();
// data from ourself or some other LyX instance
QByteArray const ar = source->data(mime);
QByteArray const ar = cache_.data(mime);
LYXERR(Debug::ACTION, "Getting from clipboard: mime = " << mime.data()
<< "length = " << ar.count());
@ -336,17 +359,13 @@ void GuiClipboard::put(string const & lyx, docstring const & text)
bool GuiClipboard::hasLyXContents() const
{
QMimeData const * const source =
qApp->clipboard()->mimeData(QClipboard::Clipboard);
return source && source->hasFormat(lyxMimeType());
return cache_.hasFormat(lyxMimeType());
}
bool GuiClipboard::hasTextContents() const
{
QMimeData const * const source =
qApp->clipboard()->mimeData(QClipboard::Clipboard);
return source && source->hasText();
return cache_.hasText();
}
@ -361,12 +380,9 @@ bool GuiClipboard::hasGraphicsContents(Clipboard::GraphicsType type) const
|| hasGraphicsContents(LinkBackGraphicsType);
}
QMimeData const * const source =
qApp->clipboard()->mimeData(QClipboard::Clipboard);
// handle image cases first
if (type == PngGraphicsType || type == JpegGraphicsType)
return source->hasImage();
return cache_.hasImage();
// handle LinkBack for Mac
if (type == LinkBackGraphicsType)
@ -377,7 +393,7 @@ bool GuiClipboard::hasGraphicsContents(Clipboard::GraphicsType type) const
#endif // Q_WS_MACX
// get mime data
QStringList const & formats = source->formats();
QStringList const & formats = cache_.formats();
LYXERR(Debug::ACTION, "We found " << formats.size() << " formats");
for (int i = 0; i < formats.size(); ++i)
LYXERR(Debug::ACTION, "Found format " << formats[i]);
@ -391,7 +407,7 @@ bool GuiClipboard::hasGraphicsContents(Clipboard::GraphicsType type) const
default: LASSERT(false, /**/);
}
return source && source->hasFormat(mime);
return cache_.hasFormat(mime);
}
@ -420,9 +436,14 @@ bool GuiClipboard::hasInternal() const
void GuiClipboard::on_dataChanged()
{
QMimeData const * const source =
qApp->clipboard()->mimeData(QClipboard::Clipboard);
QStringList l = source->formats();
//Note: we do not really need to run cache_.update() unless the
//data has been changed *and* the GuiClipboard has been queried.
//However if run cache_.update() the moment a process grabs the
//clipboard, the process holding the clipboard presumably won't
//yet be frozen, and so we won't need to wait 5 seconds for Qt
//to time-out waiting for the clipboard.
cache_.update();
QStringList l = cache_.formats();
LYXERR(Debug::ACTION, "Qt Clipboard changed. We found the following mime types:");
for (int i = 0; i < l.count(); i++)
LYXERR(Debug::ACTION, l.value(i));

View File

@ -16,13 +16,43 @@
#include "frontends/Clipboard.h"
#include <QMimeData>
#include <QObject>
#include <QStringList>
namespace lyx {
namespace frontend {
class QMacPasteboardMimeGraphics;
/**
* \class CacheMimeData
*
* This class is used in order to query the clipboard only once on
* startup and once each time the contents of the clipboard changes.
*/
class CacheMimeData : public QMimeData
{
Q_OBJECT
public:
// LyX calls "on_dataChanged" on startup, so it is not necessary to
// query the clipboard here.
CacheMimeData()
{}
/// reads the clipboard and updates the cached_formats_
void update();
/// returns the cached list of formats supported by the object
virtual QStringList formats() const { return cached_formats_; }
/// reads the clipboard and returns the data
QByteArray data(QString const & mimeType) const;
private:
/// the cached list of formats supported by the object
QStringList cached_formats_;
};
/**
* The Qt4 version of the Clipboard.
*/
@ -57,6 +87,9 @@ private:
bool text_clipboard_empty_;
bool has_lyx_contents_;
bool has_graphics_contents_;
/// the cached mime data used to describe the information
/// that can be stored in the clipboard
CacheMimeData cache_;
};
QString const lyxMimeType();