/*
Copyright (c) 2014, Lukas Holecek
This file is part of CopyQ.
CopyQ is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
CopyQ is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with CopyQ. If not, see .
*/
#include "itemdata.h"
#include "ui_itemdatasettings.h"
#include "common/contenttype.h"
#include "common/mimetypes.h"
#include "common/textdata.h"
#include
#include
#include
#include
#include
namespace {
// Limit number of characters for performance reasons.
const int defaultMaxBytes = 256;
QString hexData(const QByteArray &data)
{
if ( data.isEmpty() )
return QString();
QString result;
QString chars;
int i = 0;
forever {
if (i > 0) {
if ( (i % 2) == 0 )
result.append( QString(" ") );
if ( (i % 16) == 0 ) {
result.append(" ");
result.append(chars);
result.append( QString("\n") );
chars.clear();
if (i >= data.size() )
break;
}
}
if ( (i % 16) == 0 ) {
result.append( QString("%1: ").arg(QString::number(i, 16), 4, QChar('0')) );
}
if (i < data.size() ) {
QChar c = data[i];
result.append( QString("%1").arg(QString::number(c.unicode(), 16), 2, QChar('0')) );
chars.append( c.isPrint() ? escapeHtml(QString(c)) : QString(".") );
} else {
result.append( QString(" ") );
}
++i;
}
return result;
}
QString stringFromBytes(const QByteArray &bytes, const QString &format)
{
QTextCodec *codec = QTextCodec::codecForName("utf-8");
if (format == QLatin1String("text/html"))
codec = QTextCodec::codecForHtml(bytes, codec);
return codec->toUnicode(bytes);
}
bool emptyIntersection(const QStringList &lhs, const QStringList &rhs)
{
for (const auto &l : lhs) {
if ( rhs.contains(l) )
return false;
}
return true;
}
} // namespace
ItemData::ItemData(const QModelIndex &index, int maxBytes, QWidget *parent)
: QLabel(parent)
, ItemWidget(this)
{
setTextInteractionFlags(Qt::TextSelectableByMouse);
setContentsMargins(4, 4, 4, 4);
setTextFormat(Qt::RichText);
QString text;
const QVariantMap data = index.data(contentType::data).toMap();
for ( const auto &format : data.keys() ) {
QByteArray bytes = data[format].toByteArray();
const int size = bytes.size();
bool trimmed = size > maxBytes;
if (trimmed)
bytes = bytes.left(maxBytes);
bool hasText = format.startsWith("text/") ||
format.startsWith("application/x-copyq-owner-window-title");
const QString content = hasText ? escapeHtml(stringFromBytes(bytes, format)) : hexData(bytes);
text.append( QString("") );
text.append( QString("%1 (%2 bytes)
%3
")
.arg(format)
.arg(size)
.arg(content) );
text.append( QString("
") );
if (trimmed)
text.append( QString("...
") );
}
setText(text);
}
void ItemData::highlight(const QRegExp &, const QFont &, const QPalette &)
{
}
void ItemData::mousePressEvent(QMouseEvent *e)
{
QLabel::mousePressEvent(e);
e->ignore();
}
void ItemData::mouseDoubleClickEvent(QMouseEvent *e)
{
if ( e->modifiers().testFlag(Qt::ShiftModifier) )
QLabel::mouseDoubleClickEvent(e);
else
e->ignore();
}
void ItemData::contextMenuEvent(QContextMenuEvent *e)
{
e->ignore();
}
ItemDataLoader::ItemDataLoader()
{
}
ItemDataLoader::~ItemDataLoader() = default;
ItemWidget *ItemDataLoader::create(const QModelIndex &index, QWidget *parent, bool preview) const
{
if ( index.data(contentType::isHidden).toBool() )
return nullptr;
const QStringList formats = index.data(contentType::data).toMap().keys();
if ( emptyIntersection(formats, formatsToSave()) )
return nullptr;
const int bytes = preview ? 4096 : m_settings.value("max_bytes", defaultMaxBytes).toInt();
return new ItemData(index, bytes, parent);
}
QStringList ItemDataLoader::formatsToSave() const
{
return m_settings.contains("formats")
? m_settings["formats"].toStringList()
: QStringList() << mimeUriList << QString("text/xml");
}
QVariantMap ItemDataLoader::applySettings()
{
Q_ASSERT(ui != nullptr);
m_settings["formats"] = ui->plainTextEditFormats->toPlainText().split( QRegExp("[;,\\s]+") );
m_settings["max_bytes"] = ui->spinBoxMaxChars->value();
return m_settings;
}
QWidget *ItemDataLoader::createSettingsWidget(QWidget *parent)
{
ui.reset(new Ui::ItemDataSettings);
QWidget *w = new QWidget(parent);
ui->setupUi(w);
const QStringList formats = formatsToSave();
ui->plainTextEditFormats->setPlainText( formats.join(QString("\n")) );
ui->spinBoxMaxChars->setValue( m_settings.value("max_bytes", defaultMaxBytes).toInt() );
connect( ui->treeWidgetFormats, SIGNAL(itemActivated(QTreeWidgetItem*,int)),
SLOT(on_treeWidgetFormats_itemActivated(QTreeWidgetItem*,int)) );
return w;
}
void ItemDataLoader::on_treeWidgetFormats_itemActivated(QTreeWidgetItem *item, int column)
{
const QString mime = item->toolTip(column);
if ( !mime.isEmpty() )
ui->plainTextEditFormats->appendPlainText(mime);
}
Q_EXPORT_PLUGIN2(itemdata, ItemDataLoader)