#include "EditDialog.h"#include "ui_EditDialog.h"#include "sqlitedb.h"#include "Settings.h"#include "qhexedit.h"#include "docktextedit.h"#include "FileDialog.h"#include "Data.h"#include <QMainWindow>#include <QKeySequence>#include <QShortcut>#include <QImageReader>#include <QBuffer>#include <QModelIndex>#include <QtXml/QDomDocument>#include <QMessageBox>#include <QPrinter>#include <QPrintPreviewDialog>#include <QPainter>#include <QClipboard>#include <QTextDocument>#include <Qsci/qsciscintilla.h>#include <json.hpp>using json = nlohmann::json;EditDialog::EditDialog(QWidget* parent): QDialog(parent),ui(new Ui::EditDialog),currentIndex(QModelIndex()),dataSource(SciBuffer),dataType(Null),isReadOnly(true){ui->setupUi(this);// Add Ctrl-Enter (Cmd-Enter on OSX) as a shortcut for the Apply buttonui->buttonApply->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Return));ui->buttonApply->setToolTip(ui->buttonApply->toolTip() + " [" + ui->buttonApply->shortcut().toString(QKeySequence::NativeText) + "]");QHBoxLayout* hexLayout = new QHBoxLayout(ui->editorBinary);hexEdit = new QHexEdit(this);hexLayout->addWidget(hexEdit);hexEdit->setOverwriteMode(false);QHBoxLayout* sciLayout = new QHBoxLayout(ui->editorSci);sciEdit = new DockTextEdit(this);sciLayout->addWidget(sciEdit);QShortcut* ins = new QShortcut(QKeySequence(Qt::Key_Insert), this);connect(ins, SIGNAL(activated()), this, SLOT(toggleOverwriteMode()));connect(sciEdit, SIGNAL(textChanged()), this, SLOT(updateApplyButton()));connect(sciEdit, SIGNAL(textChanged()), this, SLOT(editTextChanged()));connect(ui->qtEdit, SIGNAL(textChanged()), this, SLOT(updateApplyButton()));connect(hexEdit, SIGNAL(dataChanged()), this, SLOT(updateApplyButton()));// Create shortcuts for the widgets that doesn't have its own print action or printing mechanism.QShortcut* shortcutPrint = new QShortcut(QKeySequence::Print, this, nullptr, nullptr, Qt::WidgetShortcut);connect(shortcutPrint, &QShortcut::activated, this, &EditDialog::openPrintDialog);// Add actions to editors that have a context menu based on actions. This also activates the shortcuts.ui->editorImage->addAction(ui->actionPrintImage);ui->editorBinary->addAction(ui->actionPrint);ui->editorBinary->addAction(ui->actionCopyHexAscii);mustIndentAndCompact = Settings::getValue("databrowser", "indent_compact").toBool();ui->actionIndent->setChecked(mustIndentAndCompact);ui->buttonAutoSwitchMode->setChecked(Settings::getValue("databrowser", "auto_switch_mode").toBool());ui->actionWordWrap->setChecked(Settings::getValue("databrowser", "editor_word_wrap").toBool());setWordWrapping(ui->actionWordWrap->isChecked());reloadSettings();}EditDialog::~EditDialog(){Settings::setValue("databrowser", "indent_compact", mustIndentAndCompact);Settings::setValue("databrowser", "auto_switch_mode", ui->buttonAutoSwitchMode->isChecked());Settings::setValue("databrowser", "editor_word_wrap", ui->actionWordWrap->isChecked());delete ui;}void EditDialog::setCurrentIndex(const QModelIndex& idx){currentIndex = QPersistentModelIndex(idx);QByteArray bArrData = idx.data(Qt::EditRole).toByteArray();loadData(bArrData);updateCellInfoAndMode(bArrData);ui->buttonApply->setDisabled(true);}void EditDialog::showEvent(QShowEvent*){// Whenever the dialog is shown, position it at the center of the parent dialogQMainWindow* parentDialog = qobject_cast<QMainWindow*>(parent());if(parentDialog){move(parentDialog->x() + parentDialog->width() / 2 - width() / 2,parentDialog->y() + parentDialog->height() / 2 - height() / 2);}}void EditDialog::reject(){// We override this, to ensure the Escape key doesn't make the Edit Cell// dock go awayreturn;}// Loads data from a cell into the Edit Cell windowvoid EditDialog::loadData(const QByteArray& bArrdata){QImage img;QString textData;// Clear previously removed BOMremovedBom.clear();// Determine the data type, saving that info in the class variabledataType = checkDataType(bArrdata);// Get the current editor mode (eg text, hex, image, json or xml mode)int editMode = ui->comboMode->currentIndex();// Data type specific handlingswitch (dataType) {case Null:// Set enabled the text widgetsciEdit->setEnabled(true);switch (editMode) {case TextEditor:case JsonEditor:case XmlEditor:// The JSON widget buffer is now the main data sourcedataSource = SciBuffer;// Empty the text editor contents, then enable text editingsciEdit->clear();break;case RtlTextEditor:// The text widget buffer is now the main data sourcedataSource = QtBuffer;// Empty the Qt text editor contents, then enable text editingui->qtEdit->clear();break;case HexEditor:// The hex widget buffer is now the main data sourcedataSource = HexBuffer;// Load the Null into the hex editorhexEdit->setData(bArrdata);break;case ImageViewer:// The hex widget buffer is now the main data sourcedataSource = HexBuffer;// Clear any image from the image viewing widgetui->editorImage->setPixmap(QPixmap(0,0));// Load the Null into the hex editorhexEdit->setData(bArrdata);break;}break;case Text:case RtlText:case JSON:case XML:// Can be stored in any widget, except the ImageViewerswitch (editMode) {case TextEditor:case JsonEditor:case XmlEditor:setDataInBuffer(bArrdata, SciBuffer);break;case RtlTextEditor:setDataInBuffer(bArrdata, QtBuffer);break;case HexEditor:setDataInBuffer(bArrdata, HexBuffer);break;case ImageViewer:// The image viewer cannot hold data nor display text.// Clear any image from the image viewing widgetui->editorImage->setPixmap(QPixmap(0,0));// Load the text into the text editorsetDataInBuffer(bArrdata, SciBuffer);break;}break;case Image:// 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 lostsetDataInBuffer(bArrdata, HexBuffer);// Update the display if in text edit or image viewer modeswitch (editMode) {case TextEditor:case XmlEditor:case JsonEditor:// Disable text editing, and use a warning message as the contentssciEdit->setText(tr("Image data can't be viewed in this mode.") % '\n' %tr("Try switching to Image or Binary mode."));sciEdit->setTextInMargin(tr("Image"));sciEdit->setEnabled(false);break;case RtlTextEditor:// Disable text editing, and use a warning message as the contentsui->qtEdit->setText(QString("<i>" %tr("Image data can't be viewed in this mode.") % "<br/>" %tr("Try switching to Image or Binary mode.") %"</i>"));ui->qtEdit->setEnabled(false);break;case ImageViewer:// Load the image into the image viewing widgetif (img.loadFromData(bArrdata)) {ui->editorImage->setPixmap(QPixmap::fromImage(img));}break;}break;case SVG:// Set the XML data in any buffer or update image in image viewer modeswitch (editMode) {case TextEditor:case JsonEditor:case XmlEditor:setDataInBuffer(bArrdata, SciBuffer);break;case HexEditor:setDataInBuffer(bArrdata, HexBuffer);break;case ImageViewer:// Set data in the XML (Sci) Buffer and load the SVG ImagesetDataInBuffer(bArrdata, SciBuffer);sciEdit->setLanguage(DockTextEdit::XML);// Load the image into the image viewing widgetif (img.loadFromData(bArrdata)) {ui->editorImage->setPixmap(QPixmap::fromImage(img));}break;case RtlTextEditor:setDataInBuffer(bArrdata, QtBuffer);break;}break;default:// The data seems to be general binary data, which is always loaded// into the hex widget (the only safe place for it)// Load the data into the hex buffersetDataInBuffer(bArrdata, HexBuffer);switch (editMode) {case TextEditor:case JsonEditor:case XmlEditor:// Disable text editing, and use a warning message as the contentssciEdit->setText(QString(tr("Binary data can't be viewed in this mode.") % '\n' %tr("Try switching to Binary mode.")));sciEdit->setTextInMargin(Settings::getValue("databrowser", "blob_text").toString());sciEdit->setEnabled(false);break;case RtlTextEditor:// Disable text editing, and use a warning message as the contentsui->qtEdit->setText(QString("<i>" %tr("Binary data can't be viewed in this mode.") % "<br/>" %tr("Try switching to Binary mode.") %"</i>"));ui->qtEdit->setEnabled(false);break;case ImageViewer:// Clear any image from the image viewing widgetui->editorImage->setPixmap(QPixmap(0,0));break;}}}void EditDialog::importData(){// Get list of supported image file formats to include them in the file dialog filterQString image_formats;QList<QByteArray> image_formats_list = QImageReader::supportedImageFormats();for(int i=0;i<image_formats_list.size();++i)image_formats.append(QString("*.%1 ").arg(QString::fromUtf8(image_formats_list.at(i))));// Chop last spaceimage_formats.chop(1);QStringList filters;filters << FILE_FILTER_TXT<< FILE_FILTER_JSON<< FILE_FILTER_XML<< tr("Image files (%1)").arg(image_formats)<< FILE_FILTER_BIN<< FILE_FILTER_ALL;QString selectedFilter;// Get the current editor mode (eg text, hex, image, json or xml mode)int mode = ui->comboMode->currentIndex();// Set as selected filter the appropriate for the current mode.switch (mode) {case TextEditor:case RtlTextEditor:selectedFilter = FILE_FILTER_TXT;break;case HexEditor:selectedFilter = FILE_FILTER_BIN;break;case ImageViewer:selectedFilter = tr("Image files (%1)").arg(image_formats);break;case JsonEditor:selectedFilter = FILE_FILTER_JSON;break;case XmlEditor:selectedFilter = FILE_FILTER_XML;break;}QString fileName = FileDialog::getOpenFileName(OpenDataFile,this,tr("Choose a file to import")#ifndef Q_OS_MAC // Filters on OS X are buggy, filters.join(";;"), &selectedFilter#endif);if(QFile::exists(fileName)){QFile file(fileName);if(file.open(QIODevice::ReadOnly)){QByteArray d = file.readAll();loadData(d);file.close();// Update the cell data info in the bottom left of the Edit Cell// and update mode (if required) to the just imported data type.updateCellInfoAndMode(d);}}}void EditDialog::exportData(){QStringList filters;switch (dataType) {case Image: {// Images get special treatment.// Determine the likely filename extension.QByteArray cellData = hexEdit->data();QBuffer imageBuffer(&cellData);QImageReader imageReader(&imageBuffer);QString imageFormat = imageReader.format();filters << tr("%1 Image").arg(imageFormat.toUpper()) % " (*." % imageFormat.toLower() % ")";break;}case Binary:filters << tr("Binary files (*.bin)");break;case RtlText:case Text:// Include the XML case on the text data type, since XML detection is not very sofisticated.if (ui->comboMode->currentIndex() == XmlEditor)filters << FILE_FILTER_XML<< FILE_FILTER_TXT;elsefilters << FILE_FILTER_TXT<< FILE_FILTER_XML;break;case JSON:filters << FILE_FILTER_JSON;break;case SVG:filters << FILE_FILTER_SVG;break;case XML:filters << FILE_FILTER_XML;break;case Null:return;}if (dataSource == HexBuffer)filters << FILE_FILTER_HEX;filters << FILE_FILTER_ALL;QString selectedFilter = filters.first();QString fileName = FileDialog::getSaveFileName(CreateDataFile,this,tr("Choose a filename to export data"),filters.join(";;"),/* defaultFileName */ QString(),&selectedFilter);if(fileName.size() > 0){QFile file(fileName);if(file.open(QIODevice::WriteOnly)){switch (dataSource) {case HexBuffer:// Data source is the hex buffer// If text option has been selected, the readable representation of the content is saved to file.if (selectedFilter == FILE_FILTER_HEX)file.write(hexEdit->toReadableString().toUtf8());elsefile.write(hexEdit->data());break;case SciBuffer:// Data source is the Scintilla bufferfile.write(sciEdit->text().toUtf8());break;case QtBuffer:// Data source is the text bufferfile.write(ui->qtEdit->toPlainText().toUtf8());break;}file.close();}}}void EditDialog::setNull(){ui->qtEdit->clear();ui->editorImage->clear();hexEdit->setData(QByteArray());sciEdit->clear();dataType = Null;removedBom.clear();// The text editors don't know the difference between an empty string// and a NULL, so we need to record NULL outside of thatdataType = Null;// Ensure the text (Scintilla) editor is enabledsciEdit->setEnabled(true);// Update the cell data info in the bottom left of the Edit Cell// The mode is also (if required) updated to text since it gives// the better visual clue of containing a NULL value (placeholder).updateCellInfoAndMode(hexEdit->data());sciEdit->setFocus();}void EditDialog::updateApplyButton(){if (!isReadOnly)ui->buttonApply->setEnabled(true);}bool EditDialog::promptInvalidData(const QString& data_type, const QString& errorString){QMessageBox::StandardButton reply = QMessageBox::question(this,tr("Invalid data for this mode"),tr("The cell contains invalid %1 data. Reason: %2. Do you really want to apply it to the cell?").arg(data_type, errorString),QMessageBox::Apply | QMessageBox::Cancel);return (reply == QMessageBox::Apply);}void EditDialog::accept(){if(!currentIndex.isValid())return;if (dataType == Null) {emit recordTextUpdated(currentIndex, hexEdit->data(), true);return;}// New data for the plain text cases. This is processed after the case.QString newTextData;switch (dataSource) {case QtBuffer:newTextData = removedBom + ui->qtEdit->toPlainText();break;case SciBuffer:switch (sciEdit->language()) {case DockTextEdit::PlainText:newTextData = removedBom + sciEdit->text();break;case DockTextEdit::JSON:{sciEdit->clearErrorIndicators();QString oldData = currentIndex.data(Qt::EditRole).toString();QString newData;bool proceed = true;json jsonDoc;try {jsonDoc = json::parse(sciEdit->text().toStdString());} catch(json::parse_error& parseError) {sciEdit->setErrorIndicator(static_cast<int>(parseError.byte - 1));proceed = promptInvalidData("JSON", parseError.what());}if (!jsonDoc.is_null()) {if (mustIndentAndCompact)// Compact the JSON data before storingnewData = QString::fromStdString(jsonDoc.dump());elsenewData = sciEdit->text();} else {newData = sciEdit->text();}proceed = proceed && (oldData != newData);if (proceed)// The data is different, so commit it back to the databaseemit recordTextUpdated(currentIndex, newData.toUtf8(), false);}break;case DockTextEdit::XML:{QString oldData = currentIndex.data(Qt::EditRole).toString();QString newData;QDomDocument xmlDoc;QString errorMsg;int errorLine, errorColumn;bool isValid = xmlDoc.setContent(sciEdit->text().toUtf8(), true, &errorMsg, &errorLine, &errorColumn);bool proceed;sciEdit->clearErrorIndicators();if (!isValid) {sciEdit->setErrorIndicator(errorLine-1, errorColumn-1, errorLine, 0);newData = sciEdit->text();proceed = (oldData != newData && promptInvalidData("XML", errorMsg));} else {if (mustIndentAndCompact)// Compact the XML data before storing. If indent is -1, no whitespace at all is added.newData = xmlDoc.toString(-1);elsenewData = sciEdit->text();proceed = (oldData != newData);}if (proceed)// The data is different, so commit it back to the databaseemit recordTextUpdated(currentIndex, newData.toUtf8(), false);}break;}break;case HexBuffer:// The data source is the hex widget buffer, thus binary dataQByteArray oldData = currentIndex.data(Qt::EditRole).toByteArray();QByteArray newData = hexEdit->data();if (newData != oldData)emit recordTextUpdated(currentIndex, newData, true);break;}if (!newTextData.isEmpty()) {QString oldData = currentIndex.data(Qt::EditRole).toString();// Check first for null case, otherwise empty strings cannot overwrite NULL valuesif ((currentIndex.data(Qt::EditRole).isNull() && dataType != Null) || oldData != newTextData)// The data is different, so commit it back to the databaseemit recordTextUpdated(currentIndex, removedBom + newTextData.toUtf8(), false);}}void EditDialog::setDataInBuffer(const QByteArray& bArrdata, DataSources source){dataSource = source;QString textData;// 1) Perform validation and text formatting (if applicable).// 2) Set the text in the corresponding editor widget (the text widget for the Image case).// 3) Enable the widget.switch (dataSource) {case QtBuffer:{// Load the text into the text editor, remove BOM first if there is oneQByteArray dataWithoutBom = bArrdata;removedBom = removeBom(dataWithoutBom);textData = QString::fromUtf8(dataWithoutBom.constData(), dataWithoutBom.size());ui->qtEdit->setPlainText(textData);// Select all of the text by default (this is useful for simple text data that we usually edit as a whole).// We don't want this when the QtBuffer has been automatically switched due to the insertion of RTL text,// (detected through the state of the apply button) otherwise that would break the typing flow of the user.if (!isReadOnly)if (!ui->buttonApply->isEnabled())ui->qtEdit->selectAll();elseui->qtEdit->moveCursor(QTextCursor::End);ui->qtEdit->setEnabled(true);break;}case SciBuffer:switch (sciEdit->language()) {case DockTextEdit::PlainText:{// Load the text into the text editor, remove BOM first if there is oneQByteArray dataWithoutBom = bArrdata;removedBom = removeBom(dataWithoutBom);textData = QString::fromUtf8(dataWithoutBom.constData(), dataWithoutBom.size());sciEdit->setText(textData);// Select all of the text by default (this is useful for simple text data that we usually edit as a whole)if (!isReadOnly)sciEdit->selectAll();sciEdit->setEnabled(true);break;}case DockTextEdit::JSON:{sciEdit->clearErrorIndicators();json jsonDoc;try {jsonDoc = json::parse(std::string(bArrdata.constData(), static_cast<size_t>(bArrdata.size())));} catch(json::parse_error& parseError) {sciEdit->setErrorIndicator(static_cast<int>(parseError.byte - 1));}if (mustIndentAndCompact && !jsonDoc.is_null() && !jsonDoc.is_discarded()) {// Load indented JSON into the JSON editortextData = QString::fromStdString(jsonDoc.dump(4));} else {// Fallback case. The data is not yet valid JSON or no auto-formatting applied.textData = QString::fromUtf8(bArrdata.constData(), bArrdata.size());}sciEdit->setText(textData);sciEdit->setEnabled(true);}break;case DockTextEdit::XML:{QString errorMsg;int errorLine, errorColumn;QDomDocument xmlDoc;bool isValid = xmlDoc.setContent(bArrdata, true, &errorMsg, &errorLine, &errorColumn);if (mustIndentAndCompact && isValid) {// Load indented XML into the XML editortextData = xmlDoc.toString(Settings::getValue("editor", "tabsize").toInt());} else {// Fallback case. The data is not yet valid JSON or no auto-formatting applied.textData = QString::fromUtf8(bArrdata.constData(), bArrdata.size());}sciEdit->setText(textData);sciEdit->clearErrorIndicators();if (!isValid)// Adjust line and column by one (Scintilla starts at 1 and QDomDocument at 0)sciEdit->setErrorIndicator(errorLine-1, errorColumn-1, errorLine, 0);sciEdit->setEnabled(true);}break;}break;case HexBuffer:hexEdit->setData(bArrdata);hexEdit->setEnabled(true);break;}}// Called when the user manually changes the "Mode" drop down comboboxvoid EditDialog::editModeChanged(int newMode){ui->actionIndent->setEnabled(newMode == JsonEditor || newMode == XmlEditor);setStackCurrentIndex(newMode);// * If the dataSource is the text buffer, the data is always text *switch (dataSource) {case QtBuffer:switch (newMode) {case RtlTextEditor: // Switching to the RTL text editor// Nothing to do, as the text is already in the Qt bufferbreak;case TextEditor: // Switching to one of the Scintilla editor modescase JsonEditor:case XmlEditor:setDataInBuffer(ui->qtEdit->toPlainText().toUtf8(), SciBuffer);break;case HexEditor: // Switching to the hex editor// Convert the text widget buffer for the hex widget// The hex widget buffer is now the main data sourcesetDataInBuffer(removedBom + ui->qtEdit->toPlainText().toUtf8(), HexBuffer);break;case ImageViewer:// Clear any image from the image viewing widgetui->editorImage->setPixmap(QPixmap(0,0));break;}break;case HexBuffer:// * If the dataSource is the hex buffer, the contents could be anything// so we just pass it to our loadData() function to handle *// Note that we have already set the editor, as loadData() relies on it// being current// Load the data into the appropriate widget, as done by loadData()loadData(hexEdit->data());break;case SciBuffer:switch (newMode) {case RtlTextEditor: // Switching to the RTL text editor// Convert the Scintilla widget buffer for the Qt widgetsetDataInBuffer(sciEdit->text().toUtf8(), QtBuffer);break;case HexEditor: // Switching to the hex editor// Convert the text widget buffer for the hex widgetsetDataInBuffer(sciEdit->text().toUtf8(), HexBuffer);break;case ImageViewer:{// When SVG format, load the image, else clear it.QByteArray bArrdata = sciEdit->text().toUtf8();dataType = checkDataType(bArrdata);if (dataType == SVG) {QImage img;if (img.loadFromData(bArrdata))ui->editorImage->setPixmap(QPixmap::fromImage(img));else// Clear any image from the image viewing widgetui->editorImage->setPixmap(QPixmap(0,0));}}break;case TextEditor: // Switching to the text editorcase JsonEditor: // Switching to the JSON editorcase XmlEditor: // Switching to the XML editor// The text is already in the Sci buffer but we need to perform the necessary formatting.setDataInBuffer(sciEdit->text().toUtf8(), SciBuffer);break;}}}// Called for every keystroke in the text editor (only)void EditDialog::editTextChanged(){if (dataSource == SciBuffer || dataSource == QtBuffer) {// Update the cell info in the bottom left manually. This is because// updateCellInfoAndMode() only works with QByteArray's (for now)int dataLength;bool isModified;if(dataSource == QtBuffer) {// QtBufferdataLength = ui->qtEdit->toPlainText().length();isModified = ui->qtEdit->document()->isModified();} else {// SciBufferdataLength = sciEdit->text().length();isModified = sciEdit->isModified();// Switch to the Qt Editor if we detect right-to-left text,// since the QScintilla editor does not support it.if (containsRightToLeft(sciEdit->text()))ui->comboMode->setCurrentIndex(RtlTextEditor);}// If data has been entered in the text editor, it can't be a NULL// any more. It hasn't been validated yet, so it cannot be JSON nor XML.if (dataType == Null && isModified && dataLength != 0)dataType = Text;if (dataType != Null) {sciEdit->clearTextInMargin();ui->labelType->setText(tr("Type of data currently in cell: Text / Numeric"));}ui->labelSize->setText(tr("%n character(s)", "", dataLength));}}void EditDialog::setMustIndentAndCompact(bool enable){mustIndentAndCompact = enable;// Indent or compact if necessary. If data has changed (button Apply indicates so), reload from the widget, else from the table.if (ui->buttonApply->isEnabled()) {setDataInBuffer(sciEdit->text().toUtf8(), SciBuffer);} elsesetCurrentIndex(currentIndex);}// Determine the type of data in the cellint EditDialog::checkDataType(const QByteArray& bArrdata){QByteArray cellData = bArrdata;// Check for NULL data typeif (cellData.isNull()) {return Null;}// Check if it's an image. First do a quick test by calling canRead() which only checks the first couple of bytes or so. Only if// that returned true, do a more sophisticated test of the data. This way we get both, good performance and proper data checking.QBuffer imageBuffer(&cellData);QImageReader readerBuffer(&imageBuffer);QString imageFormat = readerBuffer.format();if(readerBuffer.canRead() && !readerBuffer.read().isNull())return imageFormat == "svg" ? SVG : Image;// Check if it's text onlyif(isTextOnly(cellData)){if (cellData.startsWith("<?xml"))return XML;auto json_parse_result = json::parse(cellData, nullptr, false);if(!json_parse_result.is_discarded() && !json_parse_result.is_number())return JSON;else {if (containsRightToLeft(QString::fromUtf8(cellData)))return RtlText;elsereturn Text;}}// It's none of the above, so treat it as general binary datareturn Binary;}void EditDialog::toggleOverwriteMode(){static bool currentMode = false;currentMode = !currentMode;hexEdit->setOverwriteMode(currentMode);ui->qtEdit->setOverwriteMode(currentMode);sciEdit->setOverwriteMode(currentMode);}void EditDialog::setFocus(){QDialog::setFocus();// Set the focus to the editor widget. The idea here is that setting focus// to the dock itself doesn't make much sense as it's just a frame; you'd// have to tab to the editor which is what you most likely want to use. So// in order to save the user from doing this we explicitly set the focus// to the current editor.int editMode = ui->editorStack->currentIndex();switch (editMode) {case TextEditor:sciEdit->setFocus();if (sciEdit->language() == DockTextEdit::PlainText && !isReadOnly)sciEdit->selectAll();break;case RtlTextEditor:ui->qtEdit->setFocus();ui->qtEdit->selectAll();case HexEditor:hexEdit->setFocus();break;case ImageViewer:// Nothing to dobreak;}}// Enables or disables the Apply, Null, & Import buttons in the Edit Cell dock.// Sets or unsets read-only properties for the editors.void EditDialog::setReadOnly(bool ro){isReadOnly = ro;ui->buttonApply->setEnabled(!ro);ui->actionNull->setEnabled(!ro);ui->actionImport->setEnabled(!ro);ui->qtEdit->setReadOnly(ro);sciEdit->setReadOnly(ro);hexEdit->setReadOnly(ro);// This makes the caret being visible for selection, although the editor is read-only. The read-only state is hinted by the// caret not blinking. The same is done in ExtendedScintilla.Qt::TextInteractionFlags textFlags = ro? Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard : Qt::TextEditorInteraction;ui->qtEdit->setTextInteractionFlags(textFlags);}void EditDialog::switchEditorMode(bool autoSwitchForType){if (autoSwitchForType) {// Switch automatically the editing mode according to the detected data.switch (dataType) {case Image:ui->comboMode->setCurrentIndex(ImageViewer);break;case Binary:ui->comboMode->setCurrentIndex(HexEditor);break;case Null:case Text:ui->comboMode->setCurrentIndex(TextEditor);break;case RtlText:ui->comboMode->setCurrentIndex(RtlTextEditor);break;case JSON:ui->comboMode->setCurrentIndex(JsonEditor);break;case SVG:case XML:ui->comboMode->setCurrentIndex(XmlEditor);break;}}}// Update the information labels in the bottom left corner of the dialog// and switches the editor mode, if required, according to the detected data type.void EditDialog::updateCellInfoAndMode(const QByteArray& bArrdata){QByteArray cellData = bArrdata;switchEditorMode(ui->buttonAutoSwitchMode->isChecked());// Image data needs special treatmentif (dataType == Image || dataType == SVG) {QBuffer imageBuffer(&cellData);QImageReader imageReader(&imageBuffer);// Display the image formatQString imageFormat = imageReader.format();ui->labelType->setText(tr("Type of data currently in cell: %1 Image").arg(imageFormat.toUpper()));// Display the image dimensions and sizeQSize imageDimensions = imageReader.size();int imageSize = cellData.size();QString labelSizeText = tr("%1x%2 pixel(s)").arg(imageDimensions.width()).arg(imageDimensions.height()) + ", " + humanReadableSize(imageSize);ui->labelSize->setText(labelSizeText);return;}// Use a switch statement for the other data types to keep things neat :)switch (dataType) {case Null: {// NULL data typeui->labelType->setText(tr("Type of data currently in cell: NULL"));ui->labelSize->setText(tr("%n byte(s)", "", 0));// Use margin to set the NULL text.sciEdit->setTextInMargin(Settings::getValue("databrowser", "null_text").toString());break;}case XML:case Text:case RtlText: {// Text only// Determine the length of the cell text in characters (possibly different to number of bytes).int textLength = QString(cellData).length();ui->labelType->setText(tr("Type of data currently in cell: Text / Numeric"));ui->labelSize->setText(tr("%n character(s)", "", textLength));break;}case JSON: {// Valid JSON// Determine the length of the cell text in characters (possibly different to number of bytes).int jsonLength = QString(cellData).length();ui->labelType->setText(tr("Type of data currently in cell: Valid JSON"));ui->labelSize->setText(tr("%n character(s)", "", jsonLength));break;}default:// Determine the length of the cell dataint dataLength = cellData.length();// If none of the above data types, consider it general binary dataui->labelType->setText(tr("Type of data currently in cell: Binary"));ui->labelSize->setText(tr("%n byte(s)", "", dataLength));break;}}QString EditDialog::humanReadableSize(double byteCount) const{QStringList units;units << "" << "Ki" << "Mi" << "Gi" << "Ti" << "Pi" << "Ei" << "Zi";for(const QString& unit : units){if(fabs(byteCount) < 1024.0){QString size = QString::number(byteCount, 'f', 2);return size + " " + unit + "B";}byteCount /= 1024.0;}QString yiUnit = "Yi";QString size = QString::number(byteCount, 'f', 2);return size + " " + yiUnit + "B";}void EditDialog::reloadSettings(){// Set the (SQL) editor font for hex editor, since it needs a// Monospace font and the databrowser font would be usually of// variable width.QFont hexFont(Settings::getValue("editor", "font").toString());hexFont.setPointSize(Settings::getValue("databrowser", "fontsize").toInt());hexEdit->setFont(hexFont);ui->editCellToolbar->setToolButtonStyle(static_cast<Qt::ToolButtonStyle>(Settings::getValue("General", "toolbarStyleEditCell").toInt()));sciEdit->reloadSettings();}void EditDialog::setStackCurrentIndex(int editMode){switch (editMode) {case TextEditor:// Scintilla case: switch to the single Scintilla editor and set languageui->editorStack->setCurrentIndex(TextEditor);sciEdit->setLanguage(DockTextEdit::PlainText);break;case HexEditor:case ImageViewer:case RtlTextEditor:// General case: switch to the selected editorui->editorStack->setCurrentIndex(editMode);break;case JsonEditor:// Scintilla case: switch to the single Scintilla editor and set languageui->editorStack->setCurrentIndex(TextEditor);sciEdit->setLanguage(DockTextEdit::JSON);break;case XmlEditor:// Scintilla case: switch to the single Scintilla editor and set languageui->editorStack->setCurrentIndex(TextEditor);sciEdit->setLanguage(DockTextEdit::XML);break;}}void EditDialog::openPrintDialog(){int editMode = ui->editorStack->currentIndex();if (editMode == ImageViewer) {openPrintImageDialog();return;}QPrinter printer;QPrintPreviewDialog *dialog = new QPrintPreviewDialog(&printer);QTextDocument *document = new QTextDocument();switch (dataSource) {case SciBuffer:// This case isn't really expected because the Scintilla widget has it's own printing slotdocument->setPlainText(sciEdit->text());break;case HexBuffer:document->setPlainText(hexEdit->toReadableString());document->setDefaultFont(hexEdit->font());break;case QtBuffer:document->setPlainText(ui->qtEdit->toPlainText());break;}connect(dialog, &QPrintPreviewDialog::paintRequested, [&](QPrinter *previewPrinter) {document->print(previewPrinter);});dialog->exec();delete dialog;delete document;}void EditDialog::openPrintImageDialog(){QPrinter printer;QPrintPreviewDialog *dialog = new QPrintPreviewDialog(&printer);connect(dialog, &QPrintPreviewDialog::paintRequested, [&](QPrinter *previewPrinter) {QPainter painter(previewPrinter);QRect rect = painter.viewport();QSize size = ui->editorImage->pixmap()->size();size.scale(rect.size(), Qt::KeepAspectRatio);painter.setViewport(rect.x(), rect.y(), size.width(), size.height());painter.setWindow(ui->editorImage->pixmap()->rect());painter.drawPixmap(0, 0, *ui->editorImage->pixmap());});dialog->exec();delete dialog;}void EditDialog::copyHexAscii(){QApplication::clipboard()->setText(hexEdit->selectionToReadableString());}void EditDialog::setWordWrapping(bool value){// Set wrap linessciEdit->setWrapMode(value ? QsciScintilla::WrapWord : QsciScintilla::WrapNone);ui->qtEdit->setWordWrapMode(value ? QTextOption::WrapAtWordBoundaryOrAnywhere : QTextOption::NoWrap);}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。