X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Ffrontends%2Fqt4%2FGuiClipboard.cpp;h=4c8d47e7f8c5eea6c3fb6a8b18acaeacd191f29c;hb=1f10969bb5c5f36017bf5ba8671381b09945cf57;hp=438feac97bc28cf2fc5923c85e3a4b44b14f71bc;hpb=aafd52f44167d5510be1ddcb974daa9dae486933;p=lyx.git diff --git a/src/frontends/qt4/GuiClipboard.cpp b/src/frontends/qt4/GuiClipboard.cpp index 438feac97b..4c8d47e7f8 100644 --- a/src/frontends/qt4/GuiClipboard.cpp +++ b/src/frontends/qt4/GuiClipboard.cpp @@ -14,6 +14,7 @@ #include "FileDialog.h" +#include "support/FileName.h" #include "GuiClipboard.h" #include "qt_helpers.h" @@ -29,9 +30,9 @@ #include "support/lstrings.h" #include "support/lyxtime.h" -#ifdef Q_WS_MACX +#ifdef Q_OS_MAC #include "support/linkback/LinkBackProxy.h" -#endif // Q_WS_MACX +#endif // Q_OS_MAC #include "frontends/alert.h" @@ -44,6 +45,8 @@ #include #include #include +#include +#include #include @@ -59,9 +62,9 @@ namespace lyx { namespace frontend { -static QMimeData const * read_clipboard() +static QMimeData const * read_clipboard() { - LYXERR(Debug::ACTION, "Getting Clipboard"); + LYXERR(Debug::CLIPBOARD, "Getting Clipboard"); QMimeData const * source = qApp->clipboard()->mimeData(QClipboard::Clipboard); if (!source) { @@ -80,7 +83,7 @@ static QMimeData const * read_clipboard() void CacheMimeData::update() { time_t const start_time = current_time(); - LYXERR(Debug::ACTION, "Creating CacheMimeData object"); + LYXERR(Debug::CLIPBOARD, "Creating CacheMimeData object"); cached_formats_ = read_clipboard()->formats(); // Qt times out after 5 seconds if it does not recieve a response. @@ -91,13 +94,15 @@ void CacheMimeData::update() } -QByteArray CacheMimeData::data(QString const & mimeType) const +QByteArray CacheMimeData::data(QString const & mimeType) const { return read_clipboard()->data(mimeType); } QString const lyxMimeType(){ return "application/x-lyx"; } +QString const texMimeType(){ return "text/x-tex"; } +QString const latexMimeType(){ return "application/x-latex"; } QString const pdfMimeType(){ return "application/pdf"; } QString const emfMimeType(){ return "image/x-emf"; } QString const wmfMimeType(){ return "image/x-wmf"; } @@ -108,23 +113,23 @@ GuiClipboard::GuiClipboard() connect(qApp->clipboard(), SIGNAL(dataChanged()), this, SLOT(on_dataChanged())); // initialize clipboard status. - on_dataChanged(); + update(); } string const GuiClipboard::getAsLyX() const { - LYXERR(Debug::ACTION, "GuiClipboard::getAsLyX(): `"); + LYXERR(Debug::CLIPBOARD, "GuiClipboard::getAsLyX(): `"); // We don't convert encodings here since the encoding of the // clipboard contents is specified in the data itself if (cache_.hasFormat(lyxMimeType())) { // data from ourself or some other LyX instance QByteArray const ar = cache_.data(lyxMimeType()); string const s(ar.data(), ar.count()); - LYXERR(Debug::ACTION, s << "'"); + LYXERR(Debug::CLIPBOARD, s << "'"); return s; } - LYXERR(Debug::ACTION, "'"); + LYXERR(Debug::CLIPBOARD, "'"); return string(); } @@ -146,31 +151,31 @@ FileName GuiClipboard::getPastedGraphicsFileName(Cursor const & cur, types.push_back(Clipboard::PngGraphicsType); if (hasGraphicsContents(Clipboard::JpegGraphicsType)) types.push_back(Clipboard::JpegGraphicsType); - - LASSERT(!types.empty(), /**/); - + + LASSERT(!types.empty(), return FileName()); + // select prefered type if AnyGraphicsType was passed if (type == Clipboard::AnyGraphicsType) type = types.front(); - + // which extension? map extensions; map typeNames; - + extensions[Clipboard::EmfGraphicsType] = "emf"; extensions[Clipboard::WmfGraphicsType] = "wmf"; extensions[Clipboard::LinkBackGraphicsType] = "linkback"; extensions[Clipboard::PdfGraphicsType] = "pdf"; extensions[Clipboard::PngGraphicsType] = "png"; extensions[Clipboard::JpegGraphicsType] = "jpeg"; - + typeNames[Clipboard::EmfGraphicsType] = _("Enhanced Metafile"); typeNames[Clipboard::WmfGraphicsType] = _("Windows Metafile"); typeNames[Clipboard::LinkBackGraphicsType] = _("LinkBack PDF"); typeNames[Clipboard::PdfGraphicsType] = _("PDF"); typeNames[Clipboard::PngGraphicsType] = _("PNG"); typeNames[Clipboard::JpegGraphicsType] = _("JPEG"); - + // find unused filename with primary extension string document_path = cur.buffer()->fileName().onlyPath().absFileName(); unsigned newfile_number = 0; @@ -182,7 +187,7 @@ FileName GuiClipboard::getPastedGraphicsFileName(Cursor const & cur, + convert(newfile_number) + "." + extensions[type])); } while (filename.isReadableFile()); - + while (true) { // create file type filter, putting the prefered on to the front QStringList filter; @@ -195,23 +200,23 @@ FileName GuiClipboard::getPastedGraphicsFileName(Cursor const & cur, filter.append(toqstr(s)); } filter = fileFilters(filter.join(";;")); - + // show save dialog for the graphic FileDialog dlg(qt_("Choose a filename to save the pasted graphic as")); FileDialog::Result result = dlg.save(toqstr(filename.onlyPath().absFileName()), filter, toqstr(filename.onlyFileName())); - + if (result.first == FileDialog::Later) return FileName(); - + string newFilename = fromqstr(result.second); if (newFilename.empty()) { cur.bv().message(_("Canceled.")); return FileName(); } filename.set(newFilename); - + // check the extension (the user could have changed it) if (!suffixIs(ascii_lowercase(filename.absFileName()), "." + extensions[type])) { @@ -224,7 +229,7 @@ FileName GuiClipboard::getPastedGraphicsFileName(Cursor const & cur, break; } } - + // invalid extension found, or none at all. In the latter // case set the default extensions. if (i == types.size() @@ -232,7 +237,7 @@ FileName GuiClipboard::getPastedGraphicsFileName(Cursor const & cur, filename.changeExtension("." + extensions[type]); } } - + // check whether the file exists and warn the user if (!filename.exists()) break; @@ -243,10 +248,10 @@ FileName GuiClipboard::getPastedGraphicsFileName(Cursor const & cur, if (ret == 0) // overwrite, hence break the dialog loop break; - + // not overwrite, hence show the dialog again (i.e. loop) } - + return filename; } @@ -263,7 +268,7 @@ FileName GuiClipboard::getAsGraphics(Cursor const & cur, GraphicsType type) cons // get image from QImage from clipboard QImage image = qApp->clipboard()->image(); if (image.isNull()) { - LYXERR(Debug::ACTION, "No image in clipboard"); + LYXERR(Debug::CLIPBOARD, "No image in clipboard"); return FileName(); } @@ -276,11 +281,11 @@ FileName GuiClipboard::getAsGraphics(Cursor const & cur, GraphicsType type) cons else if (type == JpegGraphicsType) image.save(toqstr(filename.absFileName()), "JPEG"); else - LASSERT(false, /**/); - + LATTEST(false); + return filename; } - + // get mime for type QString mime; switch (type) { @@ -288,28 +293,28 @@ FileName GuiClipboard::getAsGraphics(Cursor const & cur, GraphicsType type) cons case LinkBackGraphicsType: mime = pdfMimeType(); break; case EmfGraphicsType: mime = emfMimeType(); break; case WmfGraphicsType: mime = wmfMimeType(); break; - default: LASSERT(false, /**/); + default: LASSERT(false, return FileName()); } - + // get data if (!cache_.hasFormat(mime)) return FileName(); // data from ourself or some other LyX instance QByteArray const ar = cache_.data(mime); - LYXERR(Debug::ACTION, "Getting from clipboard: mime = " << mime.data() + LYXERR(Debug::CLIPBOARD, "Getting from clipboard: mime = " << mime.constData() << "length = " << ar.count()); - + QFile f(toqstr(filename.absFileName())); if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) { - LYXERR(Debug::ACTION, "Error opening file " + LYXERR(Debug::CLIPBOARD, "Error opening file " << filename.absFileName() << " for writing"); return FileName(); } - + // write the (LinkBack) PDF data f.write(ar); if (type == LinkBackGraphicsType) { -#ifdef Q_WS_MACX +#ifdef Q_OS_MAC void const * linkBackData; unsigned linkBackLen; getLinkBackData(&linkBackData, &linkBackLen); @@ -319,8 +324,8 @@ FileName GuiClipboard::getAsGraphics(Cursor const & cur, GraphicsType type) cons ds << pdfLen; // big endian by default #else // only non-Mac this should never happen - LASSERT(false, /**/); -#endif // Q_WS_MACX + LATTEST(false); +#endif // Q_OS_MAC } f.close(); @@ -328,12 +333,80 @@ FileName GuiClipboard::getAsGraphics(Cursor const & cur, GraphicsType type) cons } -docstring const GuiClipboard::getAsText() const +namespace { +/** + * Tidy up a HTML chunk coming from the clipboard. + * This is needed since different applications put different kinds of HTML + * on the clipboard: + * - With or without the tag + * - With or without the tag + * - With or without the tag + * - With or without the tag + * - With or without the

tag + * Since we are going to write a HTML file for external converters we need + * to ensure that it is a well formed HTML file, including all the mentioned tags. + */ +QString tidyHtml(QString input) +{ + // Misuse QTextDocument to cleanup the HTML. + // As a side effect, all visual markup like is converted to CSS, + // which is ignored by gnuhtml2latex. + // While this may be seen as a bug by some people it is actually a + // good thing, since we do import structure, but ignore all visual + // clutter. + QTextDocument converter; + converter.setHtml(input); + return converter.toHtml("utf-8"); +} +} + + +docstring const GuiClipboard::getAsText(TextType type) const { // text data from other applications - QString const str = qApp->clipboard()->text(QClipboard::Clipboard) + if ((type == AnyTextType || type == LyXOrPlainTextType) && hasTextContents(LyXTextType)) + type = LyXTextType; + if (type == AnyTextType && hasTextContents(LaTeXTextType)) + type = LaTeXTextType; + if (type == AnyTextType && hasTextContents(HtmlTextType)) + type = HtmlTextType; + QString str; + switch (type) { + case LyXTextType: + // must not convert to docstring, since file can contain + // mixed encodings (use getAsLyX() instead) + break; + case AnyTextType: + case LyXOrPlainTextType: + case PlainTextType: + str = qApp->clipboard()->text(QClipboard::Clipboard) .normalized(QString::NormalizationForm_C); - LYXERR(Debug::ACTION, "GuiClipboard::getAsText(): `" << str << "'"); + break; + case LaTeXTextType: { + QMimeData const * source = + qApp->clipboard()->mimeData(QClipboard::Clipboard); + if (source) { + // First try LaTeX, then TeX (we do not distinguish + // for clipboard purposes) + if (source->hasFormat(latexMimeType())) { + str = source->data(latexMimeType()); + str = str.normalized(QString::NormalizationForm_C); + } else if (source->hasFormat(texMimeType())) { + str = source->data(texMimeType()); + str = str.normalized(QString::NormalizationForm_C); + } + } + break; + } + case HtmlTextType: { + QString subtype = "html"; + str = qApp->clipboard()->text(subtype, QClipboard::Clipboard) + .normalized(QString::NormalizationForm_C); + str = tidyHtml(str); + break; + } + } + LYXERR(Debug::CLIPBOARD, "GuiClipboard::getAsText(" << type << "): `" << str << "'"); if (str.isNull()) return docstring(); @@ -341,10 +414,16 @@ docstring const GuiClipboard::getAsText() const } -void GuiClipboard::put(string const & lyx, docstring const & text) +void GuiClipboard::put(string const & text) const { - LYXERR(Debug::ACTION, "GuiClipboard::put(`" << lyx << "' `" - << to_utf8(text) << "')"); + qApp->clipboard()->setText(toqstr(text)); +} + + +void GuiClipboard::put(string const & lyx, docstring const & html, docstring const & text) +{ + LYXERR(Debug::CLIPBOARD, "GuiClipboard::put(`" << lyx << "' `" + << to_utf8(html) << "' `" << to_utf8(text) << "')"); // 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; @@ -363,19 +442,33 @@ void GuiClipboard::put(string const & lyx, docstring const & text) // clipboard. QString const qtext = toqstr(text); data->setText(qtext); + QString const qhtml = toqstr(html); + data->setHtml(qhtml); qApp->clipboard()->setMimeData(data, QClipboard::Clipboard); } -bool GuiClipboard::hasLyXContents() const -{ - return cache_.hasFormat(lyxMimeType()); -} - - -bool GuiClipboard::hasTextContents() const +bool GuiClipboard::hasTextContents(Clipboard::TextType type) const { - return cache_.hasText(); + switch (type) { + case AnyTextType: + return cache_.hasFormat(lyxMimeType()) || cache_.hasText() || + cache_.hasHtml() || cache_.hasFormat(latexMimeType()) || + cache_.hasFormat(texMimeType()); + case LyXOrPlainTextType: + return cache_.hasFormat(lyxMimeType()) || cache_.hasText(); + case LyXTextType: + return cache_.hasFormat(lyxMimeType()); + case PlainTextType: + return cache_.hasText(); + case HtmlTextType: + return cache_.hasHtml(); + case LaTeXTextType: + return cache_.hasFormat(latexMimeType()) || + cache_.hasFormat(texMimeType()); + } + // shut up compiler + return false; } @@ -396,17 +489,17 @@ bool GuiClipboard::hasGraphicsContents(Clipboard::GraphicsType type) const // handle LinkBack for Mac if (type == LinkBackGraphicsType) -#ifdef Q_WS_MACX +#ifdef Q_OS_MAC return isLinkBackDataInPasteboard(); #else return false; -#endif // Q_WS_MACX - +#endif // Q_OS_MAC + // get mime data QStringList const & formats = cache_.formats(); - LYXERR(Debug::ACTION, "We found " << formats.size() << " formats"); + LYXERR(Debug::CLIPBOARD, "We found " << formats.size() << " formats"); for (int i = 0; i < formats.size(); ++i) - LYXERR(Debug::ACTION, "Found format " << formats[i]); + LYXERR(Debug::CLIPBOARD, "Found format " << formats[i]); // compute mime for type QString mime; @@ -414,16 +507,16 @@ bool GuiClipboard::hasGraphicsContents(Clipboard::GraphicsType type) const case EmfGraphicsType: mime = emfMimeType(); break; case WmfGraphicsType: mime = wmfMimeType(); break; case PdfGraphicsType: mime = pdfMimeType(); break; - default: LASSERT(false, /**/); + default: LASSERT(false, return false); } - + return cache_.hasFormat(mime); } bool GuiClipboard::isInternal() const { - if (!hasLyXContents()) + if (!hasTextContents(LyXTextType)) return false; // ownsClipboard() is also true for stuff coming from dialogs, e.g. @@ -445,11 +538,11 @@ bool GuiClipboard::isInternal() const bool GuiClipboard::hasInternal() const { // Windows and Mac OS X does not have the concept of ownership; - // the clipboard is a fully global resource so all applications + // the clipboard is a fully global resource so all applications // are notified of changes. However, on Windows ownership is // emulated by Qt through the OleIsCurrentClipboard() API, while // on Mac OS X we deal with this issue by ourself. -#if (defined(Q_WS_X11) || defined(Q_WS_WIN)) +#ifndef Q_OS_MAC return true; #else return false; @@ -458,6 +551,17 @@ bool GuiClipboard::hasInternal() const void GuiClipboard::on_dataChanged() +{ + update(); +#if defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN) + // Retry on Windows (#10109) + if (cache_.formats().count() == 0) { + QTimer::singleShot(100, this, SLOT(update())); + } +#endif +} + +void GuiClipboard::update() { //Note: we do not really need to run cache_.update() unless the //data has been changed *and* the GuiClipboard has been queried. @@ -467,14 +571,14 @@ void GuiClipboard::on_dataChanged() //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:"); + LYXERR(Debug::CLIPBOARD, "Qt Clipboard changed. We found the following mime types:"); for (int i = 0; i < l.count(); i++) - LYXERR(Debug::ACTION, l.value(i)); - - text_clipboard_empty_ = qApp->clipboard()-> + LYXERR(Debug::CLIPBOARD, l.value(i)); + + plaintext_clipboard_empty_ = qApp->clipboard()-> text(QClipboard::Clipboard).isEmpty(); - has_lyx_contents_ = hasLyXContents(); + has_text_contents_ = hasTextContents(); has_graphics_contents_ = hasGraphicsContents(); } @@ -485,9 +589,9 @@ bool GuiClipboard::empty() const // clipboard. The plaintext version is empty if the LyX version // contains only one inset, and the LyX version is empty if the // clipboard does not come from LyX. - if (!text_clipboard_empty_) + if (!plaintext_clipboard_empty_) return false; - return !has_lyx_contents_ && !has_graphics_contents_; + return !has_text_contents_ && !has_graphics_contents_; } } // namespace frontend