Merge pull request #713 from justinclift/edit_cell_perfv1

Reimplement data handling in the Edit Cell widget
This commit is contained in:
Justin Clift
2016-08-09 15:41:10 +01:00
committed by GitHub
5 changed files with 258 additions and 147 deletions

View File

@@ -10,10 +10,12 @@
#include <QShortcut>
#include <QImageReader>
#include <QBuffer>
//#include <QStringBuilder>
EditDialog::EditDialog(QWidget* parent)
: QDialog(parent),
ui(new Ui::EditDialog)
ui(new Ui::EditDialog),
dataType(Null)
{
ui->setupUi(this);
@@ -24,7 +26,6 @@ EditDialog::EditDialog(QWidget* parent)
hexEdit = new QHexEdit(this);
hexLayout->addWidget(hexEdit);
hexEdit->setOverwriteMode(false);
connect(hexEdit, SIGNAL(dataChanged()), this, SLOT(hexDataChanged()));
QShortcut* ins = new QShortcut(QKeySequence(Qt::Key_Insert), this);
connect(ins, SIGNAL(activated()), this, SLOT(toggleOverwriteMode()));
@@ -52,13 +53,10 @@ void EditDialog::reset()
ui->editorImage->clear();
hexEdit->setData(QByteArray());
oldData = "";
// Hide the warning about editing non-text data in text mode
ui->labelBinaryWarning->setVisible(false);
dataType = Null;
// Update the cell data info in the bottom left of the Edit Cell
int dataType = checkDataType();
updateCellInfo(dataType);
updateCellInfo(hexEdit->data());
}
void EditDialog::closeEvent(QCloseEvent*)
@@ -84,59 +82,169 @@ void EditDialog::reject()
return;
}
void EditDialog::loadText(const QByteArray& data, int row, int col)
// Loads data from a cell into the Edit Cell window
void EditDialog::loadData(const QByteArray& data)
{
curRow = row;
curCol = col;
oldData = data;
QImage img;
QString textData;
// Always load the data into the hex editor, as other methods in this class
// rely on it being there
hexEdit->setData(data);
// Determine the data type, saving that info in the class variable
dataType = checkDataType(data);
// Get the current editor mode (eg text, hex, or image mode)
int editMode = ui->editorStack->currentIndex();
// Only load the data into the text editor if that's the active edit mode
if (editMode == 0) {
QString textData = QString::fromUtf8(data.constData(), data.size());
ui->editorText->setPlainText(textData);
}
// Determine the data type in the cell
int dataType = checkDataType();
// Do some data type specific handling
QImage img;
// Data type specific handling
switch (dataType) {
case Text:
// If in text editor mode, select all of the text by default
if (editMode == 0) {
ui->editorText->selectAll();
}
case Null:
switch (editMode) {
case TextEditor:
// Empty the text editor contents, then enable text editing
ui->editorText->clear();
ui->editorText->setEnabled(true);
// Clear any image from the image viewing widget
ui->editorImage->setPixmap(QPixmap(0,0));
// The text widget buffer is now the main data source
dataSource = TextBuffer;
break;
case HexEditor:
// Load the Null into the hex editor
hexEdit->setData(data);
// The hex widget buffer is now the main data source
dataSource = HexBuffer;
break;
case ImageViewer:
// Clear any image from the image viewing widget
ui->editorImage->setPixmap(QPixmap(0,0));
// Load the Null into the hex editor
hexEdit->setData(data);
// The hex widget buffer is now the main data source
dataSource = HexBuffer;
break;
}
break;
case Text:
switch (editMode) {
case TextEditor:
// Load the text into the text editor
textData = QString::fromUtf8(data.constData(), data.size());
ui->editorText->setPlainText(textData);
// Enable text editing
ui->editorText->setEnabled(true);
// Select all of the text by default
ui->editorText->selectAll();
// The text widget buffer is now the main data source
dataSource = TextBuffer;
break;
case HexEditor:
// Load the text into the hex editor
hexEdit->setData(data);
// The hex widget buffer is now the main data source
dataSource = HexBuffer;
break;
case ImageViewer:
// Clear any image from the image viewing widget
ui->editorImage->setPixmap(QPixmap(0,0));
// Load the text into the text editor
textData = QString::fromUtf8(data.constData(), data.size());
ui->editorText->setPlainText(textData);
// Enable text editing
ui->editorText->setEnabled(true);
// Select all of the text by default
ui->editorText->selectAll();
// The text widget buffer is now the main data source
dataSource = TextBuffer;
break;
}
break;
case Image:
// For images, load the image into the image viewing widget
if (img.loadFromData(data)) {
ui->editorImage->setPixmap(QPixmap::fromImage(img));
// Image data is kept in the hex widget, mainly for safety. If we
// stored it in the editorImage widget instead, it would be a pixmap
// and there's no good way to restore that back to the original
// (pristine) image data. eg image metadata would be lost
hexEdit->setData(data);
// The hex widget buffer is now the main data source
dataSource = HexBuffer;
// Update the display if in text edit or image viewer mode
switch (editMode) {
case TextEditor:
// Disable text editing, and use a warning message as the contents
ui->editorText->setText(QString("<i>" %
tr("Image data can't be viewed with the text editor") %
"</i>"));
ui->editorText->setEnabled(false);
break;
case ImageViewer:
// Load the image into the image viewing widget
if (img.loadFromData(data)) {
ui->editorImage->setPixmap(QPixmap::fromImage(img));
}
break;
}
break;
default:
// Clear the image viewing widget for everything else too
ui->editorImage->setPixmap(QPixmap(0,0));
break;
}
// The data seems to be general binary data, which is always loaded
// into the hex widget (the only safe place for it)
// Show or hide the warning about editing binary data in text mode
updateBinaryEditWarning(editMode, dataType);
// Load the data into the hex editor
hexEdit->setData(data);
// The hex widget buffer is now the main data source
dataSource = HexBuffer;
switch (editMode) {
case TextEditor:
// Disable text editing, and use a warning message as the contents
ui->editorText->setText(QString("<i>" %
tr("Binary data can't be viewed with the text editor") %
"</i>"));
ui->editorText->setEnabled(false);
break;
case ImageViewer:
// Clear any image from the image viewing widget
ui->editorImage->setPixmap(QPixmap(0,0));
break;
}
}
}
// Loads data from a cell into the Edit Cell window
void EditDialog::loadDataFromCell(const QByteArray& data, int row, int col)
{
// Store the position of the being edited
curRow = row;
curCol = col;
// Store a copy of the data, so we can check if it has changed when the
// accept() method is called
oldData = data;
// Load the data into the cell
loadData(data);
// Update the cell data info in the bottom left of the Edit Cell
updateCellInfo(dataType);
updateCellInfo(data);
}
void EditDialog::importData()
@@ -160,30 +268,46 @@ void EditDialog::importData()
if(file.open(QIODevice::ReadOnly))
{
QByteArray d = file.readAll();
hexEdit->setData(d);
ui->editorText->setPlainText(d);
loadData(d);
file.close();
// Update the cell data info in the bottom left of the Edit Cell
int dataType = checkDataType();
updateCellInfo(dataType);
updateCellInfo(d);
}
}
}
void EditDialog::exportData()
{
// Images get special treatment
QString fileExt;
if (dataType == Image) {
// Determine the likely filename extension
QByteArray cellData = hexEdit->data();
QBuffer imageBuffer(&cellData);
QImageReader imageReader(&imageBuffer);
QString imageFormat = imageReader.format();
fileExt = imageFormat.toUpper() % " " % tr("Image") % "(*." % imageFormat.toLower() % ");;All files(*)";
} else {
fileExt = tr("Text files(*.txt);;All files(*)");
}
QString fileName = FileDialog::getSaveFileName(
this,
tr("Choose a filename to export data"),
tr("Text files(*.txt);;All files(*)"));
fileExt);
if(fileName.size() > 0)
{
QFile file(fileName);
if(file.open(QIODevice::WriteOnly))
{
file.write(hexEdit->data());
if (dataSource == HexBuffer) {
// Data source is the hex buffer
file.write(hexEdit->data());
} else {
// Data source is the text buffer
file.write(ui->editorText->toPlainText().toUtf8());
}
file.close();
}
}
@@ -194,70 +318,79 @@ void EditDialog::setNull()
ui->editorText->clear();
ui->editorImage->clear();
hexEdit->setData(QByteArray());
dataType = Null;
// Update the cell data info in the bottom left of the Edit Cell
int dataType = checkDataType();
updateCellInfo(dataType);
updateCellInfo(hexEdit->data());
ui->editorText->setFocus();
}
void EditDialog::accept()
{
// Don't update if the data hasn't changed
// To differentiate NULL and empty byte arrays, we also compare the NULL flag
if(hexEdit->data() != oldData || hexEdit->data().isNull() != oldData.isNull())
{
const QString dataType = ui->comboMode->currentText();
bool isBlob = dataType == tr("Binary");
emit updateRecordText(curRow, curCol, isBlob, hexEdit->data());
// Check if the current cell data is different from the original cell data
if (dataSource == TextBuffer) {
QString newData = ui->editorText->toPlainText();
if ((newData != oldData) || (newData.isNull() != oldData.isNull())) {
// The data is different, so commit it back to the database
QByteArray convertedText = newData.toUtf8();
emit recordTextUpdated(curRow, curCol, false, convertedText);
}
} else {
// The data source is the hex widget buffer, thus binary data
QByteArray newData = hexEdit->data();
if ((newData != oldData) || (newData.isNull() != oldData.isNull())) {
// The data is different, so commit it back to the database
emit recordTextUpdated(curRow, curCol, true, newData);
}
}
}
// Called when the user manually changes the "Mode" drop down combobox
void EditDialog::editModeChanged(int editMode)
void EditDialog::editModeChanged(int newMode)
{
// If the new editor mode is "text editor", load the data into it
if (editMode == 0) {
QByteArray data = hexEdit->data();
QString textData = QString::fromUtf8(data, data.size());
ui->editorText->setPlainText(textData);
// * If the dataSource is the text buffer, the data is always text *
if (dataSource == TextBuffer) {
switch (newMode) {
case TextEditor: // Switching to the text editor
// Nothing to do, as the text is already in the text buffer
break;
case HexEditor: // Switching to the hex editor
// Convert the text widget buffer for the hex widget
hexEdit->setData(ui->editorText->toPlainText().toUtf8());
// The hex widget buffer is now the main data source
dataSource = HexBuffer;
break;
case ImageViewer:
// Clear any image from the image viewing widget
ui->editorImage->setPixmap(QPixmap(0,0));
break;
}
// Switch to the selected editor
ui->editorStack->setCurrentIndex(newMode);
return;
}
// Switch to the selected editor
ui->editorStack->setCurrentIndex(editMode);
// * If the dataSource is the hex buffer, the contents could be anything
// so we just pass it to our loadData() function to handle *
if (dataSource == HexBuffer) {
// Switch to the selected editor first, as loadData() relies on it
// being current
ui->editorStack->setCurrentIndex(newMode);
// Show or hide the warning about editing binary data in text mode
int dataType = checkDataType();
updateBinaryEditWarning(editMode, dataType);
}
void EditDialog::editTextChanged()
{
if(ui->editorText->hasFocus())
{
hexEdit->blockSignals(true);
hexEdit->setData(ui->editorText->toPlainText().toUtf8());
hexEdit->blockSignals(false);
// Update the cell data info in the bottom left of the Edit Cell
int dataType = checkDataType();
updateCellInfo(dataType);
// Load the data into the appropriate widget, as done by loadData()
loadData(hexEdit->data());
}
}
void EditDialog::hexDataChanged()
{
// Update the text editor accordingly
ui->editorText->blockSignals(true);
ui->editorText->setPlainText(QString::fromUtf8(hexEdit->data().constData(), hexEdit->data().size()));
ui->editorText->blockSignals(false);
}
// Determine the type of data in the cell
int EditDialog::checkDataType()
int EditDialog::checkDataType(const QByteArray& data)
{
QByteArray cellData = hexEdit->data();
QByteArray cellData = data;
// Check for NULL data type
if (cellData.isNull()) {
@@ -311,27 +444,13 @@ void EditDialog::allowEditing(bool on)
ui->buttonImport->setEnabled(on);
}
// Shows or hides the warning about editing binary data in text mode
void EditDialog::updateBinaryEditWarning(int editMode, int dataType)
{
// If we're in the text editor, and the data type is not plain text
// display a warning about editing it
if ((editMode == 0) && ((dataType != Text) && (dataType != Null))) {
// Display the warning
ui->labelBinaryWarning->setVisible(true);
} else {
// Hide the warning
ui->labelBinaryWarning->setVisible(false);
}
}
// Update the information labels in the bottom left corner of the dialog
void EditDialog::updateCellInfo(int cellType)
void EditDialog::updateCellInfo(const QByteArray& data)
{
QByteArray cellData = hexEdit->data();
QByteArray cellData = data;
// Image data needs special treatment
if (cellType == Image) {
if (dataType == Image) {
QBuffer imageBuffer(&cellData);
QImageReader imageReader(&imageBuffer);
@@ -355,7 +474,7 @@ void EditDialog::updateCellInfo(int cellType)
int dataLength = cellData.length();
// Use a switch statement for the other data types to keep things neat :)
switch (cellType) {
switch (dataType) {
case Null:
// NULL data type
ui->labelType->setText(tr("Type of data currently in cell: NULL"));

View File

@@ -22,7 +22,7 @@ public:
public slots:
virtual void reset();
virtual void loadText(const QByteArray& data, int row, int col);
virtual void loadDataFromCell(const QByteArray& data, int row, int col);
virtual void setFocus();
virtual void reject();
virtual void allowEditing(bool on);
@@ -36,18 +36,16 @@ private slots:
virtual void exportData();
virtual void setNull();
virtual void accept();
virtual void editTextChanged();
virtual void hexDataChanged();
virtual int checkDataType();
virtual int checkDataType(const QByteArray& data);
virtual void loadData(const QByteArray& data);
virtual void toggleOverwriteMode();
virtual void editModeChanged(int editMode);
virtual void updateBinaryEditWarning(int editMode, int dataType);
virtual void updateCellInfo(int cellType);
virtual void editModeChanged(int newMode);
virtual void updateCellInfo(const QByteArray& data);
virtual QString humanReadableSize(double byteCount);
signals:
void goingAway();
void updateRecordText(int row, int col, bool isBlob, const QByteArray& data);
void recordTextUpdated(int row, int col, bool isBlob, const QByteArray& data);
private:
Ui::EditDialog* ui;
@@ -55,13 +53,26 @@ private:
QByteArray oldData;
int curCol;
int curRow;
int dataSource;
int dataType;
enum DataType {
enum DataSources {
TextBuffer,
HexBuffer
};
enum DataTypes {
Binary,
Image,
Null,
Text
};
enum EditModes {
TextEditor = 0,
HexEditor = 1,
ImageViewer = 2
};
};
#endif

View File

@@ -133,13 +133,6 @@
</property>
<widget class="QWidget" name="verticalLayout_4">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="labelBinaryWarning">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; color:#c00000;&quot;&gt;Warning: Editing binary content in text mode may result in corrupted data!&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QTextEdit" name="editorText">
<property name="font">
@@ -269,22 +262,6 @@
</hint>
</hints>
</connection>
<connection>
<sender>editorText</sender>
<signal>textChanged()</signal>
<receiver>EditDialog</receiver>
<slot>editTextChanged()</slot>
<hints>
<hint type="sourcelabel">
<x>234</x>
<y>218</y>
</hint>
<hint type="destinationlabel">
<x>362</x>
<y>309</y>
</hint>
</hints>
</connection>
<connection>
<sender>editorStack</sender>
<signal>currentChanged(int)</signal>

View File

@@ -99,7 +99,6 @@ void EditTableDialog::populateFields()
}
typeBox->setCurrentIndex(index);
connect(typeBox, SIGNAL(currentIndexChanged(int)), this, SLOT(updateTypes()));
//connect(typeBox, SIGNAL(editTextChanged(QString)), this, SLOT(updateTypes()));
ui->treeWidget->setItemWidget(tbitem, kType, typeBox);
tbitem->setCheckState(kNotNull, f->notnull() ? Qt::Checked : Qt::Unchecked);
@@ -464,7 +463,6 @@ void EditTableDialog::addField()
ui->treeWidget->setItemWidget(tbitem, kType, typeBox);
connect(typeBox, SIGNAL(currentIndexChanged(int)), this, SLOT(updateTypes()));
//connect(typeBox, SIGNAL(editTextChanged(QString)), this, SLOT(updateTypes()));
tbitem->setCheckState(kNotNull, Qt::Unchecked);
tbitem->setCheckState(kPrimaryKey, Qt::Unchecked);

View File

@@ -203,7 +203,7 @@ void MainWindow::init()
connect(ui->dataTable->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(setRecordsetLabel()));
connect(ui->dataTable->horizontalHeader(), SIGNAL(sectionResized(int,int,int)), this, SLOT(updateBrowseDataColumnWidth(int,int,int)));
connect(editDock, SIGNAL(goingAway()), this, SLOT(editDockAway()));
connect(editDock, SIGNAL(updateRecordText(int, int, bool, QByteArray)), this, SLOT(updateRecordText(int, int, bool, QByteArray)));
connect(editDock, SIGNAL(recordTextUpdated(int, int, bool, QByteArray)), this, SLOT(updateRecordText(int, int, bool, QByteArray)));
connect(ui->dbTreeWidget->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(changeTreeSelection()));
connect(ui->dataTable->horizontalHeader(), SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showDataColumnPopupMenu(QPoint)));
connect(ui->dataTable->verticalHeader(), SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showRecordPopupMenu(QPoint)));
@@ -840,7 +840,10 @@ void MainWindow::doubleClickTable(const QModelIndex& index)
editDock->allowEditing(allowEditing);
// Load the current value into the edit dock
editDock->loadText(index.data(Qt::EditRole).toByteArray(), index.row(), index.column());
QByteArray cellData = index.data(Qt::EditRole).toByteArray();
int cellRow = index.row();
int cellColumn = index.column();
editDock->loadDataFromCell(cellData, cellRow, cellColumn);
// Show the edit dock
ui->dockEdit->setVisible(true);
@@ -863,7 +866,10 @@ void MainWindow::dataTableSelectionChanged(const QModelIndex& index)
// If the Edit Cell dock is visible, load the new value into it
if (editDock->isVisible()) {
editDock->loadText(index.data(Qt::EditRole).toByteArray(), index.row(), index.column());
QByteArray cellData = index.data(Qt::EditRole).toByteArray();
int cellRow = index.row();
int cellColumn = index.column();
editDock->loadDataFromCell(cellData, cellRow, cellColumn);
}
}