Polish the codecs example
- Port to Qt 5 connection syntax. - Remove unneeded member variables. - Adapt to screen size. - Add a tab widget with a hex dump view to the preview dialog. - Handle conversion errors in preview dialog, add status label displaying errors and warnings about failures and invalid characters encountered. - Fix translated messages. Change-Id: I916100c903e73d0d2326523753ed7398b1c34df0 Reviewed-by: Topi Reiniö <topi.reinio@theqtcompany.com>
This commit is contained in:
parent
2a24c3c268
commit
d5be0d3058
@ -45,8 +45,8 @@
|
||||
|
||||
MainWindow::MainWindow()
|
||||
{
|
||||
textEdit = new QTextEdit;
|
||||
textEdit->setLineWrapMode(QTextEdit::NoWrap);
|
||||
textEdit = new QPlainTextEdit;
|
||||
textEdit->setLineWrapMode(QPlainTextEdit::NoWrap);
|
||||
setCentralWidget(textEdit);
|
||||
|
||||
findCodecs();
|
||||
@ -54,54 +54,57 @@ MainWindow::MainWindow()
|
||||
previewForm = new PreviewForm(this);
|
||||
previewForm->setCodecList(codecs);
|
||||
|
||||
createActions();
|
||||
createMenus();
|
||||
|
||||
setWindowTitle(tr("Codecs"));
|
||||
resize(500, 400);
|
||||
|
||||
const QRect screenGeometry = QApplication::desktop()->screenGeometry(this);
|
||||
resize(screenGeometry.width() / 2, screenGeometry.height() * 2 / 3);
|
||||
}
|
||||
|
||||
void MainWindow::open()
|
||||
{
|
||||
QString fileName = QFileDialog::getOpenFileName(this);
|
||||
if (!fileName.isEmpty()) {
|
||||
QFile file(fileName);
|
||||
if (!file.open(QFile::ReadOnly)) {
|
||||
QMessageBox::warning(this, tr("Codecs"),
|
||||
tr("Cannot read file %1:\n%2")
|
||||
.arg(fileName)
|
||||
.arg(file.errorString()));
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray data = file.readAll();
|
||||
|
||||
previewForm->setEncodedData(data);
|
||||
if (previewForm->exec())
|
||||
textEdit->setPlainText(previewForm->decodedString());
|
||||
const QString fileName = QFileDialog::getOpenFileName(this);
|
||||
if (fileName.isEmpty())
|
||||
return;
|
||||
QFile file(fileName);
|
||||
if (!file.open(QFile::ReadOnly)) {
|
||||
QMessageBox::warning(this, tr("Codecs"),
|
||||
tr("Cannot read file %1:\n%2")
|
||||
.arg(QDir::toNativeSeparators(fileName),
|
||||
file.errorString()));
|
||||
return;
|
||||
}
|
||||
|
||||
const QByteArray data = file.readAll();
|
||||
|
||||
previewForm->setWindowTitle(tr("Choose Encoding for %1").arg(QFileInfo(fileName).fileName()));
|
||||
previewForm->setEncodedData(data);
|
||||
if (previewForm->exec())
|
||||
textEdit->setPlainText(previewForm->decodedString());
|
||||
}
|
||||
|
||||
void MainWindow::save()
|
||||
{
|
||||
QString fileName = QFileDialog::getSaveFileName(this);
|
||||
if (!fileName.isEmpty()) {
|
||||
QFile file(fileName);
|
||||
if (!file.open(QFile::WriteOnly | QFile::Text)) {
|
||||
QMessageBox::warning(this, tr("Codecs"),
|
||||
tr("Cannot write file %1:\n%2")
|
||||
.arg(fileName)
|
||||
.arg(file.errorString()));
|
||||
return;
|
||||
}
|
||||
const QAction *action = qobject_cast<const QAction *>(sender());
|
||||
const QByteArray codecName = action->data().toByteArray();
|
||||
const QString title = tr("Save As (%1)").arg(QLatin1String(codecName));
|
||||
|
||||
QAction *action = qobject_cast<QAction *>(sender());
|
||||
QByteArray codecName = action->data().toByteArray();
|
||||
|
||||
QTextStream out(&file);
|
||||
out.setCodec(codecName.constData());
|
||||
out << textEdit->toPlainText();
|
||||
QString fileName = QFileDialog::getSaveFileName(this, title);
|
||||
if (fileName.isEmpty())
|
||||
return;
|
||||
QFile file(fileName);
|
||||
if (!file.open(QFile::WriteOnly | QFile::Text)) {
|
||||
QMessageBox::warning(this, tr("Codecs"),
|
||||
tr("Cannot write file %1:\n%2")
|
||||
.arg(QDir::toNativeSeparators(fileName),
|
||||
file.errorString()));
|
||||
return;
|
||||
}
|
||||
|
||||
QTextStream out(&file);
|
||||
out.setCodec(codecName.constData());
|
||||
out << textEdit->toPlainText();
|
||||
}
|
||||
|
||||
void MainWindow::about()
|
||||
@ -133,9 +136,9 @@ void MainWindow::findCodecs()
|
||||
QString sortKey = codec->name().toUpper();
|
||||
int rank;
|
||||
|
||||
if (sortKey.startsWith("UTF-8")) {
|
||||
if (sortKey.startsWith(QLatin1String("UTF-8"))) {
|
||||
rank = 1;
|
||||
} else if (sortKey.startsWith("UTF-16")) {
|
||||
} else if (sortKey.startsWith(QLatin1String("UTF-16"))) {
|
||||
rank = 2;
|
||||
} else if (iso8859RegExp.exactMatch(sortKey)) {
|
||||
if (iso8859RegExp.cap(1).size() == 1)
|
||||
@ -145,58 +148,38 @@ void MainWindow::findCodecs()
|
||||
} else {
|
||||
rank = 5;
|
||||
}
|
||||
sortKey.prepend(QChar('0' + rank));
|
||||
sortKey.prepend(QLatin1Char('0' + rank));
|
||||
|
||||
codecMap.insert(sortKey, codec);
|
||||
}
|
||||
codecs = codecMap.values();
|
||||
}
|
||||
|
||||
void MainWindow::createActions()
|
||||
void MainWindow::createMenus()
|
||||
{
|
||||
openAct = new QAction(tr("&Open..."), this);
|
||||
QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
|
||||
QAction *openAct =
|
||||
fileMenu->addAction(tr("&Open..."), this, &MainWindow::open);
|
||||
openAct->setShortcuts(QKeySequence::Open);
|
||||
connect(openAct, SIGNAL(triggered()), this, SLOT(open()));
|
||||
|
||||
foreach (QTextCodec *codec, codecs) {
|
||||
QString text = tr("%1...").arg(QString(codec->name()));
|
||||
|
||||
QAction *action = new QAction(text, this);
|
||||
action->setData(codec->name());
|
||||
connect(action, SIGNAL(triggered()), this, SLOT(save()));
|
||||
QMenu *saveAsMenu = fileMenu->addMenu(tr("&Save As"));
|
||||
connect(saveAsMenu, &QMenu::aboutToShow,
|
||||
this, &MainWindow::aboutToShowSaveAsMenu);
|
||||
foreach (const QTextCodec *codec, codecs) {
|
||||
const QByteArray name = codec->name();
|
||||
QAction *action = saveAsMenu->addAction(tr("%1...").arg(QLatin1String(name)));
|
||||
action->setData(QVariant(name));
|
||||
connect(action, &QAction::triggered, this, &MainWindow::save);
|
||||
saveAsActs.append(action);
|
||||
}
|
||||
|
||||
exitAct = new QAction(tr("E&xit"), this);
|
||||
exitAct->setShortcuts(QKeySequence::Quit);
|
||||
connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));
|
||||
|
||||
aboutAct = new QAction(tr("&About"), this);
|
||||
connect(aboutAct, SIGNAL(triggered()), this, SLOT(about()));
|
||||
|
||||
aboutQtAct = new QAction(tr("About &Qt"), this);
|
||||
connect(aboutQtAct, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
|
||||
}
|
||||
|
||||
void MainWindow::createMenus()
|
||||
{
|
||||
saveAsMenu = new QMenu(tr("&Save As"), this);
|
||||
foreach (QAction *action, saveAsActs)
|
||||
saveAsMenu->addAction(action);
|
||||
connect(saveAsMenu, SIGNAL(aboutToShow()),
|
||||
this, SLOT(aboutToShowSaveAsMenu()));
|
||||
|
||||
fileMenu = new QMenu(tr("&File"), this);
|
||||
fileMenu->addAction(openAct);
|
||||
fileMenu->addMenu(saveAsMenu);
|
||||
fileMenu->addSeparator();
|
||||
fileMenu->addAction(exitAct);
|
||||
QAction *exitAct = fileMenu->addAction(tr("E&xit"), this, &QWidget::close);
|
||||
exitAct->setShortcuts(QKeySequence::Quit);
|
||||
|
||||
helpMenu = new QMenu(tr("&Help"), this);
|
||||
helpMenu->addAction(aboutAct);
|
||||
helpMenu->addAction(aboutQtAct);
|
||||
|
||||
menuBar()->addMenu(fileMenu);
|
||||
menuBar()->addSeparator();
|
||||
menuBar()->addMenu(helpMenu);
|
||||
|
||||
QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
|
||||
helpMenu->addAction(tr("&About"), this, &MainWindow::about);
|
||||
helpMenu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt);
|
||||
}
|
||||
|
@ -46,9 +46,8 @@
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QAction;
|
||||
class QMenu;
|
||||
class QTextCodec;
|
||||
class QTextEdit;
|
||||
class QPlainTextEdit;
|
||||
QT_END_NAMESPACE
|
||||
class PreviewForm;
|
||||
|
||||
@ -67,21 +66,12 @@ private slots:
|
||||
|
||||
private:
|
||||
void findCodecs();
|
||||
void createActions();
|
||||
void createMenus();
|
||||
|
||||
QTextEdit *textEdit;
|
||||
QList<QAction *> saveAsActs;
|
||||
QPlainTextEdit *textEdit;
|
||||
PreviewForm *previewForm;
|
||||
QList<QTextCodec *> codecs;
|
||||
|
||||
QMenu *fileMenu;
|
||||
QMenu *helpMenu;
|
||||
QMenu *saveAsMenu;
|
||||
QAction *openAct;
|
||||
QList<QAction *> saveAsActs;
|
||||
QAction *exitAct;
|
||||
QAction *aboutAct;
|
||||
QAction *aboutQtAct;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -42,47 +42,159 @@
|
||||
|
||||
#include "previewform.h"
|
||||
|
||||
// Helpers for creating hex dumps
|
||||
static void indent(QTextStream &str, int indent)
|
||||
{
|
||||
for (int i = 0; i < indent; ++i)
|
||||
str << ' ';
|
||||
}
|
||||
|
||||
static void formatHex(QTextStream &str, const QByteArray &data)
|
||||
{
|
||||
const int fieldWidth = str.fieldWidth();
|
||||
const QTextStream::FieldAlignment alignment = str.fieldAlignment();
|
||||
const int base = str.integerBase();
|
||||
const QChar padChar = str.padChar();
|
||||
str.setIntegerBase(16);
|
||||
str.setPadChar(QLatin1Char('0'));
|
||||
str.setFieldAlignment(QTextStream::AlignRight);
|
||||
|
||||
const unsigned char *p = reinterpret_cast<const unsigned char *>(data.constBegin());
|
||||
for (const unsigned char *end = p + data.size(); p < end; ++p) {
|
||||
str << ' ';
|
||||
str.setFieldWidth(2);
|
||||
str << unsigned(*p);
|
||||
str.setFieldWidth(fieldWidth);
|
||||
}
|
||||
str.setFieldAlignment(alignment);
|
||||
str.setPadChar(padChar);
|
||||
str.setIntegerBase(base);
|
||||
}
|
||||
|
||||
static void formatPrintableCharacters(QTextStream &str, const QByteArray &data)
|
||||
{
|
||||
for (int i = 0, size = data.size(); i < size; ++i) {
|
||||
const char c = data.at(i);
|
||||
switch (c) {
|
||||
case '\0':
|
||||
str << "\\0";
|
||||
break;
|
||||
case '\t':
|
||||
str << "\\t";
|
||||
break;
|
||||
case '\r':
|
||||
str << "\\r";
|
||||
break;
|
||||
case '\n':
|
||||
str << "\\n";
|
||||
break;
|
||||
default:
|
||||
if (c >= 32 && uchar(c) < 127)
|
||||
str << ' ' << c;
|
||||
else
|
||||
str << "..";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static QString formatHexDump(const QByteArray &data)
|
||||
{
|
||||
enum { lineWidth = 16 };
|
||||
QString result;
|
||||
QTextStream str(&result);
|
||||
str.setIntegerBase(16);
|
||||
str.setPadChar(QLatin1Char('0'));
|
||||
const int fieldWidth = str.fieldWidth();
|
||||
const QTextStream::FieldAlignment alignment = str.fieldAlignment();
|
||||
for (int a = 0, size = data.size(); a < size; a += lineWidth) {
|
||||
str.setFieldAlignment(QTextStream::AlignRight);
|
||||
str.setFieldWidth(8);
|
||||
str << a;
|
||||
str.setFieldWidth(fieldWidth);
|
||||
str.setFieldAlignment(alignment);
|
||||
|
||||
const int end = qMin(a + lineWidth, size);
|
||||
const QByteArray line = data.mid(a, end - a);
|
||||
|
||||
formatHex(str, line);
|
||||
indent(str, 3 * (lineWidth - line.size()));
|
||||
|
||||
str << ' ';
|
||||
formatPrintableCharacters(str, line);
|
||||
indent(str, 2 * (lineWidth - line.size()));
|
||||
str << '\n';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
PreviewForm::PreviewForm(QWidget *parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
encodingComboBox = new QComboBox;
|
||||
|
||||
encodingLabel = new QLabel(tr("&Encoding:"));
|
||||
QLabel *encodingLabel = new QLabel(tr("&Encoding:"));
|
||||
encodingLabel->setBuddy(encodingComboBox);
|
||||
|
||||
textEdit = new QTextEdit;
|
||||
textEdit->setLineWrapMode(QTextEdit::NoWrap);
|
||||
textEdit = new QPlainTextEdit;
|
||||
textEdit->setLineWrapMode(QPlainTextEdit::NoWrap);
|
||||
textEdit->setReadOnly(true);
|
||||
hexDumpEdit = new QPlainTextEdit;
|
||||
hexDumpEdit->setLineWrapMode(QPlainTextEdit::NoWrap);
|
||||
hexDumpEdit->setReadOnly(true);
|
||||
hexDumpEdit->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
|
||||
|
||||
buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok
|
||||
| QDialogButtonBox::Cancel);
|
||||
QDialogButtonBox *buttonBox =
|
||||
new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
okButton = buttonBox->button(QDialogButtonBox::Ok);
|
||||
|
||||
connect(encodingComboBox, SIGNAL(activated(int)),
|
||||
this, SLOT(updateTextEdit()));
|
||||
connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
||||
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
|
||||
typedef void(QComboBox::*ComboBoxIntSignal)(int);
|
||||
connect(encodingComboBox, static_cast<ComboBoxIntSignal>(&QComboBox::activated),
|
||||
this, &PreviewForm::updateTextEdit);
|
||||
connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
|
||||
QGridLayout *mainLayout = new QGridLayout;
|
||||
QGridLayout *mainLayout = new QGridLayout(this);
|
||||
mainLayout->addWidget(encodingLabel, 0, 0);
|
||||
mainLayout->addWidget(encodingComboBox, 0, 1);
|
||||
mainLayout->addWidget(textEdit, 1, 0, 1, 2);
|
||||
mainLayout->addWidget(buttonBox, 2, 0, 1, 2);
|
||||
setLayout(mainLayout);
|
||||
tabWidget = new QTabWidget;
|
||||
tabWidget->addTab(textEdit, tr("Preview"));
|
||||
tabWidget->addTab(hexDumpEdit, tr("Hex Dump"));
|
||||
mainLayout->addWidget(tabWidget, 1, 0, 1, 2);
|
||||
statusLabel = new QLabel;
|
||||
mainLayout->addWidget(statusLabel, 2, 0, 1, 2);
|
||||
mainLayout->addWidget(buttonBox, 3, 0, 1, 2);
|
||||
|
||||
setWindowTitle(tr("Choose Encoding"));
|
||||
resize(400, 300);
|
||||
const QRect screenGeometry = QApplication::desktop()->screenGeometry(this);
|
||||
resize(screenGeometry.width() * 2 / 5, screenGeometry.height() / 2);
|
||||
}
|
||||
|
||||
void PreviewForm::setCodecList(const QList<QTextCodec *> &list)
|
||||
{
|
||||
encodingComboBox->clear();
|
||||
foreach (QTextCodec *codec, list)
|
||||
encodingComboBox->addItem(codec->name(), codec->mibEnum());
|
||||
foreach (const QTextCodec *codec, list) {
|
||||
encodingComboBox->addItem(QLatin1String(codec->name()),
|
||||
QVariant(codec->mibEnum()));
|
||||
}
|
||||
}
|
||||
|
||||
void PreviewForm::reset()
|
||||
{
|
||||
decodedStr.clear();
|
||||
textEdit->clear();
|
||||
hexDumpEdit->clear();
|
||||
statusLabel->clear();
|
||||
statusLabel->setStyleSheet(QString());
|
||||
okButton->setEnabled(false);
|
||||
tabWidget->setCurrentIndex(0);
|
||||
}
|
||||
|
||||
void PreviewForm::setEncodedData(const QByteArray &data)
|
||||
{
|
||||
reset();
|
||||
encodedData = data;
|
||||
hexDumpEdit->setPlainText(formatHexDump(data));
|
||||
updateTextEdit();
|
||||
}
|
||||
|
||||
@ -90,12 +202,30 @@ void PreviewForm::updateTextEdit()
|
||||
{
|
||||
int mib = encodingComboBox->itemData(
|
||||
encodingComboBox->currentIndex()).toInt();
|
||||
QTextCodec *codec = QTextCodec::codecForMib(mib);
|
||||
const QTextCodec *codec = QTextCodec::codecForMib(mib);
|
||||
const QString name = QLatin1String(codec->name());
|
||||
|
||||
QTextStream in(&encodedData);
|
||||
in.setAutoDetectUnicode(false);
|
||||
in.setCodec(codec);
|
||||
decodedStr = in.readAll();
|
||||
QTextCodec::ConverterState state;
|
||||
decodedStr = codec->toUnicode(encodedData.constData(), encodedData.size(), &state);
|
||||
|
||||
textEdit->setPlainText(decodedStr);
|
||||
bool success = true;
|
||||
if (state.remainingChars) {
|
||||
success = false;
|
||||
const QString message =
|
||||
tr("%1: conversion error at character %2")
|
||||
.arg(name).arg(encodedData.size() - state.remainingChars + 1);
|
||||
statusLabel->setText(message);
|
||||
statusLabel->setStyleSheet(QStringLiteral("background-color: \"red\";"));
|
||||
} else if (state.invalidChars) {
|
||||
statusLabel->setText(tr("%1: %n invalid characters", 0, state.invalidChars).arg(name));
|
||||
statusLabel->setStyleSheet(QStringLiteral("background-color: \"yellow\";"));
|
||||
} else {
|
||||
statusLabel->setText(tr("%1: %n bytes converted", 0, encodedData.size()).arg(name));
|
||||
statusLabel->setStyleSheet(QString());
|
||||
}
|
||||
if (success)
|
||||
textEdit->setPlainText(decodedStr);
|
||||
else
|
||||
textEdit->clear();
|
||||
okButton->setEnabled(success);
|
||||
}
|
||||
|
@ -48,8 +48,10 @@ QT_BEGIN_NAMESPACE
|
||||
class QComboBox;
|
||||
class QDialogButtonBox;
|
||||
class QLabel;
|
||||
class QPlainTextEdit;
|
||||
class QPushButton;
|
||||
class QTabWidget;
|
||||
class QTextCodec;
|
||||
class QTextEdit;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class PreviewForm : public QDialog
|
||||
@ -57,7 +59,7 @@ class PreviewForm : public QDialog
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PreviewForm(QWidget *parent = 0);
|
||||
explicit PreviewForm(QWidget *parent = Q_NULLPTR);
|
||||
|
||||
void setCodecList(const QList<QTextCodec *> &list);
|
||||
void setEncodedData(const QByteArray &data);
|
||||
@ -67,13 +69,17 @@ private slots:
|
||||
void updateTextEdit();
|
||||
|
||||
private:
|
||||
void reset();
|
||||
|
||||
QByteArray encodedData;
|
||||
QString decodedStr;
|
||||
|
||||
QPushButton *okButton;
|
||||
QTabWidget *tabWidget;
|
||||
QComboBox *encodingComboBox;
|
||||
QLabel *encodingLabel;
|
||||
QTextEdit *textEdit;
|
||||
QDialogButtonBox *buttonBox;
|
||||
QPlainTextEdit *textEdit;
|
||||
QPlainTextEdit *hexDumpEdit;
|
||||
QLabel *statusLabel;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user