完成微波仿真、点云构网、模型导出、仿真成像功能移植

master
剑古敛锋 2024-04-11 16:44:17 +08:00
parent a6b5ce7003
commit 51341c8e72
702 changed files with 250267 additions and 0 deletions

106
icons.qrc Normal file
View File

@ -0,0 +1,106 @@
<RCC>
<qresource prefix="/icons">
<file alias="db_new">database_add.png</file>
<file alias="db_open">database_go.png</file>
<file alias="db_revert">database_refresh.png</file>
<file alias="db_save">database_save.png</file>
<file alias="table_create">table_add.png</file>
<file alias="table_delete">table_delete.png</file>
<file alias="table_modify">table_edit.png</file>
<file alias="index_create">tag_blue_add.png</file>
<file alias="index_delete">tag_blue_delete.png</file>
<file alias="field_edit">page_edit.png</file>
<file alias="field_delete">page_delete.png</file>
<file alias="field_add">page_add.png</file>
<file alias="field">page_green.png</file>
<file alias="table">table.png</file>
<file alias="index">tag_blue.png</file>
<file alias="refresh">view-refresh.png</file>
<file alias="view_delete">picture_delete.png</file>
<file alias="view">picture.png</file>
<file alias="view_create">picture_add.png</file>
<file alias="trigger">script.png</file>
<file alias="trigger_create">script_add.png</file>
<file alias="trigger_delete">script_delete.png</file>
<file alias="settings">wrench.png</file>
<file alias="whatis">help.png</file>
<file alias="open_tab">tab_add.png</file>
<file alias="run">resultset_next.png</file>
<file alias="save_sql">page_save.png</file>
<file alias="open_sql">page_white_database.png</file>
<file alias="load_extension">plugin_add.png</file>
<file alias="remove_extension">plugin_delete.png</file>
<file alias="save_table">table_save.png</file>
<file alias="run_line">resultset_last.png</file>
<file alias="toolbar">layout_sidebar.png</file>
<file alias="down">bullet_arrow_down.png</file>
<file alias="up">bullet_arrow_up.png</file>
<file alias="appicon">sqlitebrowser.png</file>
<file alias="browser_open">internet-web-browser.png</file>
<file>package.png</file>
<file alias="project_open">package_go.png</file>
<file alias="field_key">page_key.png</file>
<file alias="encryption">key.png</file>
<file alias="document_open">document-open.png</file>
<file alias="function">chart_curve.png</file>
<file alias="keyword">cog.png</file>
<file alias="clear_filters">clear_filters.png</file>
<file alias="copy">page_copy.png</file>
<file>resultset_previous.png</file>
<file>resultset_first.png</file>
<file alias="view_modify">picture_edit.png</file>
<file alias="trigger_modify">script_edit.png</file>
<file alias="index_modify">tag_blue_edit.png</file>
<file>folder.png</file>
<file>database.png</file>
<file>cog_go.png</file>
<file alias="paste">page_paste.png</file>
<file>folder_user.png</file>
<file alias="push_database">server_go.png</file>
<file alias="find">page_find.png</file>
<file alias="close">cross.png</file>
<file alias="special_copy">page_white_copy.png</file>
<file alias="sql_copy">page_copy_sql.png</file>
<file alias="text_replace">text_replace.png</file>
<file alias="image_save">picture_save.png</file>
<file alias="log_dock">application_side_list.png</file>
<file alias="db_attach">database_link.png</file>
<file alias="text_indent">text_indent.png</file>
<file alias="print">printer.png</file>
<file alias="project_save">package_save.png</file>
<file alias="cancel">cancel.png</file>
<file alias="comment_block">comment_block.png</file>
<file alias="hourglass">hourglass.png</file>
<file alias="delete_record">table_row_delete.png</file>
<file alias="add_record">table_row_insert.png</file>
<file alias="set_to_null">textfield_delete.png</file>
<file alias="filter">filter.png</file>
<file alias="tab">tab.png</file>
<file alias="project_save_as">package_rename.png</file>
<file alias="field_fk">page_foreign_key.png</file>
<file alias="save_all">save_all.png</file>
<file alias="word_wrap">page_white_text.png</file>
<file>color_swatch.png</file>
<file>edit_cond_formats.png</file>
<file alias="clear_sorting">clear_sorting.png</file>
<file alias="arrow_bottom">bullet_arrow_bottom.png</file>
<file alias="arrow_top">bullet_arrow_top.png</file>
<file>text_bold.png</file>
<file>text_italic.png</file>
<file>text_underline.png</file>
<file>text_align_center.png</file>
<file>text_align_justify.png</file>
<file>text_align_left.png</file>
<file>text_align_right.png</file>
<file alias="background_color">page_paintbrush.png</file>
<file alias="foreground_color">text_paintbrush.png</file>
<file alias="cond_formats">style.png</file>
<file alias="edit_cond_formats">style_edit.png</file>
<file alias="clear_cond_formats">style_delete.png</file>
<file alias="add_cond_format">style_add.png</file>
<file alias="open_in_app">application_link.png</file>
<file alias="document_link">document-link.png</file>
<file alias="open_data_in_app">application_go.png</file>
<file alias="clone_database">server_add.png</file>
</qresource>
</RCC>

View File

@ -0,0 +1,86 @@
#include "CMDExcuteApp.h"
#include <QProcess>
#include <QDebug>
#include <QThread>
#include <QTimer>
#include <QString>
#include "ui_CMDExcuteApp.h"
CMDExcuteApp::CMDExcuteApp(QWidget* parent)
{
this->ui=new Ui::cmdExcuteWindows ;
this->ui->setupUi(this);
this->cmd = new QProcess();
connect(this->cmd, SIGNAL(readyReadStandardOutput()), this, SLOT(on_readoutput()));
connect(this->cmd, SIGNAL(readyReadStandardError()), this, SLOT(on_readerror()));
}
CMDExcuteApp::~CMDExcuteApp()
{
if (nullptr != this->cmd) {
delete this->cmd;
this->cmd = nullptr;
}
}
int CMDExcuteApp::excuteCmd(QString cmdText)
{
qDebug() << u8"执行命令:" << cmdText ;
this->ui->textEdit->append("cmd.exe\n");
this->cmd->start("cmd.exe");
this->cmd->waitForStarted(); //等待程序启动
this->ui->textEdit->append(QString::QString(cmdText));
this->ui->textEdit->append("\n");
this->cmd->write(cmdText.toUtf8().constData());
this->cmd->close();
this->waitExcutedFinish();
return 0;
}
int CMDExcuteApp::excuteCmd(QString exePath, QString params)
{
qDebug() << u8"执行命令:" << exePath<<u8" "<< params;
this->setWindowTitle(exePath);
QString program = QString::QString(exePath);
QStringList prams_txt;
prams_txt.append(QString::QString(params));
this->cmd->start(program, prams_txt);
//this->cmd->waitForFinished();
this->show();
this->waitExcutedFinish();
return 0;
}
int CMDExcuteApp::waitExcutedFinish()
{
while (!this->cmd->waitForFinished()) {
qDebug() << u8"运行状态:" << this->cmd->state();
QCoreApplication::processEvents();
}
qDebug() << u8"退出循环运行状态:" << this->cmd->state();
qDebug() << u8"退出循环码:" << this->cmd->exitCode();
this->on_readerror();
this->on_readoutput();
emit this->callbackExcuteResult();
return 0;
}
int CMDExcuteApp::on_readerror()
{
QString out = this->cmd->readAllStandardError().data();
qDebug()<<u8"on_readerror:\t" << out;
this->ui->textEdit->append(out);
return 0;
}
int CMDExcuteApp::on_readoutput()
{
QString out = this->cmd->readAllStandardOutput().data();
qDebug() << u8"on_readoutput:\t" << out;
this->ui->textEdit->append(out); //将输出信息读取到编辑框
return 0;
}

View File

@ -0,0 +1,47 @@
#pragma once
#ifndef CMDEXCUTEAPP_H
#define CMDEXCUTEAPP_H
#include "WBFZExchangePluginAPI.h"
#include <QObject>
#include <QtWidgets/QMainWindow>
#include <QProcess>
class QObject;
class QMainWindow;
class MOCCMDEXCUTEAPP:public QObject{
Q_OBJECT
public:
MOCCMDEXCUTEAPP()=default;
~MOCCMDEXCUTEAPP()=default;
};
namespace Ui{
class cmdExcuteWindows;
}
class CMDExcuteApp : public QMainWindow
{
Q_OBJECT
public:
CMDExcuteApp(QWidget* parent = nullptr);
~CMDExcuteApp();
int excuteCmd(QString cmdText);
int excuteCmd(QString exePath,QString params);
int waitExcutedFinish();
private:
Ui::cmdExcuteWindows* ui;
QProcess* cmd;
signals:
void callbackExcuteResult();
private slots:
int on_readoutput();
int on_readerror();
};
#endif

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>cmdExcuteWindows</class>
<widget class="QMainWindow" name="cmdExcuteWindows">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>568</width>
<height>260</height>
</rect>
</property>
<property name="windowTitle">
<string>cmd</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QTextEdit" name="textEdit"/>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,394 @@
#include "EchoTableEditWindow.h"
#include "TableProcess/TableMainWindow.h"
#include "TableProcess/TableViewModel.h"
#include "SharedModuleLib/BaseUiTool.h"
#include "LAMPImageCreateClass.h"
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <QFileDialog>
#include <QClipboard>
#include <QItemSelectionModel>
#include <QDebug>
#include "ui_EchoTableEditWindow.h"
EchoTableEditWindow::EchoTableEditWindow(QWidget* parent)
{
DebugInfo(" EchoTableEditWindow::EchoTableEditWindow has init\n");
this->FILEOPENLOCK = false;
this->CheckFieldContextHasEmptyCeilLOCK = false;
DebugInfo(" EchoTableEditWindow::EchoTableEditWindow has init ui\n");
this->ui=new Ui::EchoTableEditWindow ;
this->ui->setupUi(this);
DebugInfo(" EchoTableEditWindow::EchoTableEditWindow has init ui finish\n");
this->setWindowTitle(u8"FEKO Simulation Echo Result Convert ");
this->initTableViewContextMenu();
this->initTableViewStatusBarControl();
DebugInfo(" EchoTableEditWindow::EchoTableEditWindow has init initTableViewContextMenu finish\n");
// 定标常数界面关闭
this->ui->tab_calibration->setEnabled(false);
this->ui->tabWidget->removeTab(1);// 删除定标常数界面
DebugInfo(" EchoTableEditWindow::EchoTableEditWindow has init finish\n");
}
EchoTableEditWindow::~EchoTableEditWindow()
{
delete this->statusprogressBar;
delete this->tableViewContextMenu;
delete this->m_undoStack;
}
int EchoTableEditWindow::initTableViewContextMenu()
{
//qDebug() << u8"正在初始化contextMenu";
m_undoStack = new QUndoStack(this); //存放命令的栈
this->ui->tableView->setContextMenuPolicy(Qt::CustomContextMenu);
//this->ui->tableView->setFocusPolicy(Qt::NoFocus); // 允许快捷键
this->tableViewContextMenu = new QMenu(this->ui->tableView); // 表格控件的右键菜单
QAction* m_undoAction = m_undoStack->createUndoAction(this, u8"撤销");//添加QActionCtrl-Z作为回撤的快捷键
m_undoAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Z));
QObject::connect(m_undoAction, SIGNAL(triggered()), this, SLOT(tableView_UndoAction()));
QAction* m_redoAction = m_undoStack->createRedoAction(this, u8"重做");//添加QActionCtrl-Y左右前进的快捷键
m_redoAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Y));
QObject::connect(m_redoAction, SIGNAL(triggered()), this, SLOT(tableView_RedoAction()));
this->tableViewContextMenu->addAction(m_undoAction);
this->tableViewContextMenu->addAction(m_redoAction);
QAction *copyAction= this->tableViewContextMenu->addAction(u8"复制"); // 复制
copyAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_C));
QObject::connect(copyAction, SIGNAL(triggered()), this, SLOT(tableView_CopyAction()));
QAction* PasteAction = this->tableViewContextMenu->addAction(u8"粘贴"); // 粘贴
PasteAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_V));
QObject::connect(PasteAction, SIGNAL(triggered()), this, SLOT(tableView_PasteAction()));
QAction* CheckFieldHasEmptyCeilAction = this->tableViewContextMenu->addAction(u8"检查单元格为空");
QObject::connect(CheckFieldHasEmptyCeilAction, SIGNAL(triggered()), this, SLOT(tableView_CheckFieldHasEmptyCeilAction()));
//qDebug() << u8"初始化contextMenu结束";
return 0;
}
void EchoTableEditWindow::ShowTableViewContextMenu(QPoint p)
{
//qDebug() << u8"正在展示tableview 右键菜单";
//Q_UNUSED(pos);
this->tableViewContextMenu->exec(QCursor::pos());
}
void EchoTableEditWindow::tableView_CopyAction()
{
//qDebug() << u8"正在启动tableview 右键复制代码";
QItemSelectionModel* selectionModel = this->ui->tableView->selectionModel();
QModelIndexList selectedIndexes = selectionModel->selectedIndexes();
if (selectedIndexes.count() == 0) {
return;
}
QTableView* tableView = this->ui->tableView; // Your table view object
/*
* a\tb\tc\n
* d\tf\te\n
**/
int min_row, max_row, min_col, max_col;
min_row = selectedIndexes.at(0).row();
max_row = selectedIndexes.at(0).row();
min_col = selectedIndexes.at(0).column();
max_col = selectedIndexes.at(0).column();
for (int i = 0; i < selectedIndexes.count(); i++) {
min_row = min_row > selectedIndexes.at(i).row() ? selectedIndexes.at(i).row() : min_row;
max_row = max_row < selectedIndexes.at(i).row() ? selectedIndexes.at(i).row() : max_row;
min_col = min_col > selectedIndexes.at(i).column() ? selectedIndexes.at(i).column() : min_col;
max_col = max_col < selectedIndexes.at(i).column() ? selectedIndexes.at(i).column() : max_col;
}
std::vector<std::vector<QString>> copylist(max_row-min_row+1);
for (int i = min_row; i <= max_row; i++) {
copylist[i - min_row] = std::vector<QString>(max_col - min_col + 1);
}
// qDebug() << u8"minRow"<<min_row;
// qDebug() << u8"maxRow" << max_row;
// qDebug() << u8"minCol" << min_col;
// qDebug() << u8"maxCol" << max_col;
for (int i = 0; i < selectedIndexes.count(); i++) {
QString tempstring= selectedIndexes.at(i).data().toString();
copylist[selectedIndexes.at(i).row() - min_row]
[selectedIndexes.at(i).column() - min_col] = tempstring;
}
QString clipboardText = "";
for (int i = 0; i < copylist.size(); i++) {
for(int j=0;j<copylist[i].size()-1;j++){
clipboardText = clipboardText + copylist[i][j]+u8"\t";
}
clipboardText = clipboardText + copylist[i][copylist[i].size() - 1] + u8"\n";
//clipboardData.append("\n");
}
// Add header data
//QHeaderView* header = tableView->horizontalHeader();
//for (int i = 0; i < header->count(); i++) {
// QVariant headerData = header->model()->headerData(i, Qt::Horizontal);
// clipboardData.insert(i, headerData.toString());
//}
// qDebug() << u8"============================";
// qDebug() << clipboardText;
// qDebug() << u8"============================";
QApplication::clipboard()->setText(clipboardText);
}
int EchoTableEditWindow::setDragDropOverwriteMode(bool flag) {
this->ui->tableView->setDragDropOverwriteMode(flag);
return 0;
}
int EchoTableEditWindow::setTablCalibrationTab(bool flag)
{
if (flag) {
this->ui->tab_calibration->setEnabled(true);
this->ui->tabWidget->insertTab(1, this->ui->tab_calibration, u8"定标常数");
}
else {
this->ui->tab_calibration->setEnabled(false);
this->ui->tabWidget->removeTab(1);// 删除定标常数界面
}
return 0;
}
void EchoTableEditWindow::tableView_PasteAction() {
// qDebug() << u8"正在启动tableview 右键粘贴代码";
QTableView* tableview = this->ui->tableView;
QClipboard* clipboard = QApplication::clipboard();
QString clipboardData = QApplication::clipboard()->text();
QItemSelectionModel* selectionModel = this->ui->tableView->selectionModel();
QModelIndexList selectedIndexes = selectionModel->selectedIndexes();
this->m_undoStack->push(new PasteCommand(this->ui->tableView, selectedIndexes, clipboardData));
tableview->show();
}
void EchoTableEditWindow::tableView_UndoAction()
{
// qDebug() << u8"正在撤销命令";
int index = this->m_undoStack->index();
this->m_undoStack->setIndex(index);
}
void EchoTableEditWindow::tableView_RedoAction()
{
// qDebug() << u8"正在重做命令";
int index = this->m_undoStack->index();
this->m_undoStack->setIndex(index );
}
// 检查存在空单元格的列,并修改展示
void EchoTableEditWindow::tableView_CheckFieldHasEmptyCeilAction()
{
this->ui->statusbar->showMessage(u8"正在检查存在空数据的单元列");
size_t colcount = this->ui->tableView->model()->columnCount();
size_t rowcount = this->ui->tableView->model()->rowCount();
this->statusprogressBar->setRange(0, colcount - 1);
this->statusprogressBar->setValue(0);
QList<QModelIndex> cellIndexes;
for (int j = 0; j < colcount; j++) {
for (int i = 0; i < rowcount; i++) {
if (this->ui->tableView->model()->index(i, j).data().toString().count() < 1) {
cellIndexes.append(this->ui->tableView->model()->index(i, j));
break;
}
}
this->statusprogressBar->setValue(j);
}
if (cellIndexes.count() == 0) {
return;
}
else {}
this->ui->statusbar->showMessage(u8"正在选择空格");
this->statusprogressBar->setRange(0, cellIndexes.count()-1);
this->statusprogressBar->setValue(0);
QItemSelectionModel* selectionModel = this->ui->tableView->selectionModel();
for (int i = 0; i < cellIndexes.count(); i++) {
selectionModel->select(cellIndexes[i], QItemSelectionModel::Select);
this->statusprogressBar->setValue(i);
}
this->ui->tableView->show();
this->ui->tableView->scrollTo(cellIndexes[0], QAbstractItemView::EnsureVisible);
}
void EchoTableEditWindow::on_actionOpen_triggered()
{
this->OpenCSVDialog();
}
void EchoTableEditWindow::on_actionSave_triggered()
{
this->SaveCSVDialog();
}
void EchoTableEditWindow::on_actionSaveAs_triggered()
{
this->SaveAsDialog();
}
void EchoTableEditWindow::on_actionEchoSplitExport_triggered()
{
// 在窗口关闭事件中询问用户是否关闭
QMessageBox::StandardButton reply;
qDebug() << u8"检查乱码文件所在位置";
reply = QMessageBox::question(this, u8"询问", u8"导出回波文件前,请保存文件", QMessageBox::Yes | QMessageBox::No);
if (reply == QMessageBox::Yes) {
// 用户点击了"Yes"按钮,关闭窗口
this->SaveCSVDialog();
}
else {
// 用户点击了"No"按钮,取消关闭操作
return;
}
// 保存并导出回波文件
this->ui->statusbar->showMessage(u8"正在分析回波文件");
this->statusprogressBar->setRange(0,100);
this->statusprogressBar->setValue(10);
FEKOBase::NearFieldEchoCSVParser nearfilePraseclass;
QString echocsvfilepath = this->model->getCSVPath();
if (!nearfilePraseclass.parseCSV(echocsvfilepath)) {
QMessageBox::warning(this, u8"警告", u8"回波文件结构解析错误,请检查文件");
return;
}
this->statusprogressBar->setValue(25);
QMessageBox::information(this, u8"信息", u8"请分别为theta极化、phi极化、R极化分量回波指定保存路径");
QString thetafilepath = getSaveFilePath(
this,
QString::fromUtf8(u8"另存为"),
QString::fromUtf8(u8"theta文件 (*.theta)"));//多组扩展名用双分号";;"隔开
QString phifilepath = getSaveFilePath(
this,
QString::fromUtf8(u8"另存为"),
QString::fromUtf8(u8"phi文件 (*.phi)"));//多组扩展名用双分号";;"隔开
QString Rfilepath = getSaveFilePath(
this,
QString::fromUtf8(u8"另存为"),
QString::fromUtf8(u8"R文件 (*.R)"));//多组扩展名用双分号";;"隔开
this->ui->statusbar->showMessage(u8"正在导出theta极化");
nearfilePraseclass.toThetapolar(thetafilepath);
this->statusprogressBar->setValue(50);
this->ui->statusbar->showMessage(u8"正在导出phi极化");
nearfilePraseclass.toPhiPolar(phifilepath);
this->statusprogressBar->setValue(75);
this->ui->statusbar->showMessage(u8"正在导出R极化");
nearfilePraseclass.toRPolar(Rfilepath);
this->statusprogressBar->setValue(99);
QMessageBox::information(this, u8"信息", u8"极化回波已经保存完毕");
// 询问用户是否关闭窗口
reply = QMessageBox::question(this, u8"提示", u8"是否直接进行成像?",QMessageBox::Yes | QMessageBox::No);
if (reply == QMessageBox::Yes) {
LAMPImageCreateClass* imagewindows = new LAMPImageCreateClass;
imagewindows->show();
}
else {
return;
}
}
void EchoTableEditWindow::on_actionCalibrationConst_triggered()
{
}
int EchoTableEditWindow::LockFileOpen(bool flag)
{
this->FILEOPENLOCK = flag;
return 0;
}
int EchoTableEditWindow::setCheckFieldContextHasEmptyCeilLOCK(bool flag)
{
this->CheckFieldContextHasEmptyCeilLOCK = flag;
return 0;
}
int EchoTableEditWindow::loadTablemode(std::shared_ptr<AbstractTableModel> mode)
{
this->model = mode;
this->ui->tableView->setModel(this->model.get());
if (this->model->rowCount() > 1e4) {
this->ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
}
return 0;
}
int EchoTableEditWindow::setTableViewAutoSort(bool flag)
{
this->ui->tableView->setSortingEnabled(flag);
return flag;
}
int EchoTableEditWindow::initTableViewStatusBarControl()
{
this->statusprogressBar = new QProgressBar();
this->statusprogressBar->setRange(0, 100); // 设置进度条范围
this->statusprogressBar->setValue(0); // 设置当前进度
this->ui->statusbar->addPermanentWidget(this->statusprogressBar); // 在状态栏中添加进度条
return 0;
}
int EchoTableEditWindow::OpenCSVDialog()
{
if (this->FILEOPENLOCK) {
return 0;
}
else {}
QString tableFilepath = getOpenFilePath(
this,
QString::fromUtf8(u8"打开表格文件"),
QString::fromUtf8(u8"csv文件 (*.csv);;xls文件(*.xls)"));
this->ui->statusbar->showMessage(u8"正在打开文件....");
std::shared_ptr< FEKOResultCsvTableModel> tablemode = std::make_shared< FEKOResultCsvTableModel>();
tablemode->loadCSVFilePath(tableFilepath);
this->loadTablemode(tablemode);
this->setTableViewAutoSort(true);
this->LockFileOpen();
this->ui->statusbar->showMessage(u8"文件打开结束");
return -1;
}
int EchoTableEditWindow::SaveCSVDialog()
{
this->model->saveFilePath();
return 0;
}
int EchoTableEditWindow::SaveAsDialog()
{
QString filepath = getSaveFilePath(
this,
QString::fromUtf8(u8"另存为"),
QString::fromUtf8(u8"csv文件 (*.csv)"));//多组扩展名用双分号";;"隔开
this->model->saveAsFilePath(filepath);
return 0;
}

View File

@ -0,0 +1,102 @@
#pragma once
#ifndef ECHOTABLEEDITWINDOW_H
#define ECHOTABLEEDITWINDOW_H
#include "WBFZExchangePluginAPI.h"
#include "AllHead.h"
#include <QtWidgets/QMainWindow>
#include "TableProcess/TableViewModel.h"
#include <QUndoStack>
#include <QUndoCommand>
#include <qprogressbar.h>
#include <memory>
class QObject;
class QMainWindow;
/*
*
***/
namespace Ui {
class EchoTableEditWindow;
}
/*
*
****/
class EchoTableEditWindow : public QMainWindow
{
Q_OBJECT
public:
bool FILEOPENLOCK;
bool CheckFieldContextHasEmptyCeilLOCK;
private:
std::shared_ptr<AbstractTableModel> model;
QMenu* tableViewContextMenu;
QUndoStack* m_undoStack;
/*菜单*/
QProgressBar* statusprogressBar;
public:
EchoTableEditWindow(QWidget* parent = nullptr);
~EchoTableEditWindow();
int initTableViewStatusBarControl();
int initTableViewContextMenu(); // 初始化上下文菜单
int LockFileOpen(bool flag=true); // 锁定文件,不允许进行重新打开文件
int setCheckFieldContextHasEmptyCeilLOCK(bool flag=true);// 允许启动当前字段中存在空格检查,-- 提供给FEKOResult分析使用
int loadTablemode(std::shared_ptr<AbstractTableModel> mode); // 加载tableview 使用的model
int setTableViewAutoSort(bool flag);
int setDragDropOverwriteMode(bool flag); // 是否禁用列调换
int setTablCalibrationTab(bool flag);
private:
Ui::EchoTableEditWindow* ui;
int OpenCSVDialog();
int SaveCSVDialog();
int SaveAsDialog();
public slots:
// tableview 右键菜单
void ShowTableViewContextMenu(QPoint p);
void tableView_CopyAction();
void tableView_PasteAction();
void tableView_UndoAction();
void tableView_RedoAction();
void tableView_CheckFieldHasEmptyCeilAction();
void on_actionOpen_triggered();
void on_actionSave_triggered();
void on_actionSaveAs_triggered();
void on_actionEchoSplitExport_triggered(); // 回波数据导出
void on_actionCalibrationConst_triggered(); // 计算定标常数
protected: // 重写函数
void closeEvent(QCloseEvent* event) override {
// 在窗口关闭事件中询问用户是否关闭
QMessageBox::StandardButton reply;
reply = QMessageBox::question(this, u8"Close", u8"您回波数据导出了吗?", QMessageBox::Yes | QMessageBox::No);
if (reply == QMessageBox::Yes) {
// 用户点击了"Yes"按钮,关闭窗口
event->accept();
}
else {
// 用户点击了"No"按钮,取消关闭操作
event->ignore();
}
};
};
#endif // !ECHOTABLEEDITWINDOW_H

View File

@ -0,0 +1,285 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EchoTableEditWindow</class>
<widget class="QMainWindow" name="EchoTableEditWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>752</width>
<height>520</height>
</rect>
</property>
<property name="windowTitle">
<string>回波表格处理</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="enabled">
<bool>true</bool>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tabEcho">
<attribute name="title">
<string>回波文件</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTableView" name="tableView"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_calibration">
<attribute name="title">
<string>定标常数</string>
</attribute>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="minimumSize">
<size>
<width>0</width>
<height>25</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>25</height>
</size>
</property>
<property name="text">
<string>定标球大小</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="lineEdit_ballSize">
<property name="minimumSize">
<size>
<width>0</width>
<height>25</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>25</height>
</size>
</property>
<property name="text">
<string>0.001</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QTableView" name="tableView_calibration"/>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<widget class="QToolBar" name="toolBar">
<property name="windowTitle">
<string>toolBar</string>
</property>
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>752</width>
<height>26</height>
</rect>
</property>
<widget class="QMenu" name="menu">
<property name="title">
<string>文件</string>
</property>
<addaction name="actionOpen"/>
<addaction name="actionSave"/>
<addaction name="actionSaveAs"/>
</widget>
<widget class="QMenu" name="menu_4">
<property name="title">
<string>回波工具</string>
</property>
<addaction name="actionFEKOCheck"/>
<addaction name="actionEchoSplitExport"/>
<addaction name="actionEchoCalibration"/>
</widget>
<widget class="QMenu" name="menu_2">
<property name="title">
<string>定标工具</string>
</property>
<addaction name="actionCalibrationConst"/>
<addaction name="actionImportCalibrationCons"/>
</widget>
<addaction name="menu"/>
<addaction name="menu_4"/>
<addaction name="menu_2"/>
</widget>
<action name="actionOpen">
<property name="text">
<string>打开文件</string>
</property>
</action>
<action name="actionSave">
<property name="text">
<string>保存文件</string>
</property>
</action>
<action name="actionSaveAs">
<property name="text">
<string>另存为</string>
</property>
</action>
<action name="actionShowAllField">
<property name="text">
<string>展示所有字段</string>
</property>
</action>
<action name="actionAddField">
<property name="text">
<string>添加字段</string>
</property>
</action>
<action name="actionRemoveField">
<property name="text">
<string>移除字段</string>
</property>
</action>
<action name="actionEditField">
<property name="text">
<string>编辑字段</string>
</property>
</action>
<action name="actionCreateNewDoc">
<property name="text">
<string>创建新文件</string>
</property>
</action>
<action name="actionFEKOCheck">
<property name="text">
<string>FEKO检查</string>
</property>
</action>
<action name="action_forestObjectives">
<property name="text">
<string>森林目标属性表检查</string>
</property>
</action>
<action name="action_cropObjectives">
<property name="text">
<string>农作物目标属性表检查</string>
</property>
</action>
<action name="action_grasslandObjectives">
<property name="text">
<string>草地目标属性表检查</string>
</property>
</action>
<action name="action_waterBodyObjectives">
<property name="text">
<string>水体目标属性表检查</string>
</property>
</action>
<action name="action_soilObjectives">
<property name="text">
<string>土壤目标属性表检查</string>
</property>
</action>
<action name="action_dynamicWaterBodyObjectives">
<property name="text">
<string>动态水体目标属性表检查</string>
</property>
</action>
<action name="action_roadObjectives">
<property name="text">
<string>道路目标属性表检查</string>
</property>
</action>
<action name="action_artificialObjectives">
<property name="text">
<string>人工目标属性表检查</string>
</property>
</action>
<action name="action_geometricCorrectionScene">
<property name="text">
<string>几何校正场景属性表检查</string>
</property>
</action>
<action name="action_radiometricCorrectionScene">
<property name="text">
<string>辐射校正场景属性表检查</string>
</property>
</action>
<action name="action_landSurfaceScene">
<property name="text">
<string>陆表场景属性表检查</string>
</property>
</action>
<action name="action_waterBodyScene">
<property name="text">
<string>水体场景属性表检查</string>
</property>
</action>
<action name="action_vegetationScene">
<property name="text">
<string>植被场景属性表检查</string>
</property>
</action>
<action name="actionExportThetaData">
<property name="text">
<string>ExportThetaData</string>
</property>
</action>
<action name="actionexportData">
<property name="text">
<string>exportData</string>
</property>
</action>
<action name="actionEchoSplitExport">
<property name="text">
<string>回波切分导出</string>
</property>
</action>
<action name="actionEchoCalibration">
<property name="text">
<string>回波定位</string>
</property>
</action>
<action name="actionCalibrationConst">
<property name="text">
<string>计算定标常数</string>
</property>
</action>
<action name="actionImportCalibrationCons">
<property name="text">
<string>导入定标常数表</string>
</property>
</action>
</widget>
<resources/>
<connections/>
<slots>
<slot>OpenCSVDialog()</slot>
<slot>SaveCSVDialog()</slot>
<slot>SaveAsDialog()</slot>
<slot>ShowTableViewContextMenu(QPoint)</slot>
</slots>
</ui>

View File

@ -0,0 +1,427 @@
#include "FEKOResultImport.h"
#include "FileOperator.h"
#include "SharedModuleLib/BaseUiTool.h"
#include "TaskTreeClass.h"
#include "EchoTableEditWindow.h"
#include <QFileDialog>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <memory>
#include <boost/filesystem.hpp>
#include <QStandardItemModel>
#include <QStringListModel>
#include <QMessageBox>
#include <unordered_set>
#include <QProcess>
#include <QDebug>
#include "CMDExcuteApp.h"
#include <QtCore>
#include <QtXml>
#include <QDomDocument>
#include "ui_FEKOResultImport.h"
FEKOResultImport::FEKOResultImport(QWidget* parent)
{
//this->setAttribute(Qt::WidgetAttribute::WA_DeleteOnClose);
//this->setWindowModality(Qt::WindowModality::WindowModal);
this->ui=new Ui::FEKOResultImportWindow ;this->ui->setupUi(this);
this->ListViewmodel = new QStandardItemModel(this);
this->ui->listView->setModel(this->ListViewmodel);
this->selectListViewmodel = new QStandardItemModel(this);
this->selectListViewmodel->clear();
this->ListViewmodel->clear();
this->p = nullptr;
}
FEKOResultImport::~FEKOResultImport()
{
if (nullptr == this->ListViewmodel || NULL == this->ListViewmodel) {}
else {
this->ListViewmodel->clear();
delete this->ListViewmodel;
this->ListViewmodel = nullptr;
}
if (nullptr == this->selectListViewmodel || NULL == this->selectListViewmodel) {}
else {
this->selectListViewmodel->clear(); // 释放所有对象的内存
delete this->selectListViewmodel;
this->selectListViewmodel = nullptr;
}
if (nullptr == this->p) {}
else {
this->p->close();
//delete this->p;
this->p = nullptr;
}
}
QString FEKOResultImport::getFEKOPreFileName()
{
return this->FEKOPreFileName;
return QString();
}
int FEKOResultImport::setFEKOPreFileName(QString preFileName)
{
this->FEKOPreFileName = preFileName;
return 0;
}
int FEKOResultImport::setNearFieldNames(std::vector<QString> nearFieldNames)
{
this->nearFieldNames.clear();
this->nearFieldNames.shrink_to_fit();
this->nearFieldNames = nearFieldNames;
return 0;
}
std::vector<QString> FEKOResultImport::getNearFieldNames()
{
return this->nearFieldNames;
}
int FEKOResultImport::setFarFieldNames(std::vector<QString> farFieldNames)
{
this->farFieldNames.clear();
this->farFieldNames.shrink_to_fit();
this->farFieldNames = farFieldNames;
return 0;
}
std::vector<QString> FEKOResultImport::getFarFieldNames()
{
return this->farFieldNames;
}
int FEKOResultImport::setSelectFieldNames(std::vector<QString> SelectFieldNames, bool nearOrfar)
{
if (nearOrfar) {
this->ui->NearradioButton->setChecked(1);
this->ui->FarradioButton->setChecked(0);
}
else {
this->ui->NearradioButton->setChecked(0);
this->ui->FarradioButton->setChecked(1);
}
// 剔除数据
this->ListViewmodel->clear();
this->selectListViewmodel->clear();
// 重新分配数据
for (int i = 0; i < SelectFieldNames.size(); i++) {
QStandardItem* item = new QStandardItem(QString::QString(SelectFieldNames[i]));
this->selectListViewmodel->appendRow(item);
}
std::unordered_set<QString> selectSET;
for (int i = 0; i < SelectFieldNames.size(); i++) {
selectSET.insert(SelectFieldNames[i]);// 插入
}
if (nearOrfar) {
for (int i = 0; i < this->nearFieldNames.size(); i++)
{
if (selectSET.insert(this->nearFieldNames[i]).second)//判断是否能插入成功
{
QStandardItem* item = new QStandardItem(QString::QString(this->nearFieldNames[i]));
this->ListViewmodel->appendRow(item);
}
else {
}
}
}
else {
for (int i = 0; i < this->farFieldNames.size(); i++)
{
if (selectSET.insert(this->farFieldNames[i]).second)//判断是否能插入成功
{
QStandardItem* item = new QStandardItem(QString::QString(this->farFieldNames[i]));
this->ListViewmodel->appendRow(item);
}
else {
}
}
}
this->ui->listView->setModel(this->ListViewmodel);
this->ui->SelectlistView->setModel(this->selectListViewmodel);
return 0;
}
std::vector<QString> FEKOResultImport::getSelectFieldNames()
{
std::vector<QString> result(0);
this->selectListViewmodel = (QStandardItemModel*)(this->ui->SelectlistView->model());
int selectCount = this->ui->SelectlistView->model()->rowCount();
for (int i = 0; i < selectCount; i++) {
QStandardItem* listItem = this->selectListViewmodel->item(i);
result.push_back(listItem->text());
}
return result;
}
int FEKOResultImport::setFEKOResultCSVPath(QString csvPath)
{
this->FEKOResultCSVPath = csvPath;
return 0;
}
QString FEKOResultImport::getFEKOResultCSVPath()
{
return this->FEKOResultCSVPath;
}
int FEKOResultImport::setFEKOPreProjectFolderPath(QString FolderPath)
{
this->FEKOProjectFolderPath = FolderPath;
return 0;
}
QString FEKOResultImport::getFEKOPreProjectFolderPath()
{
return this->FEKOProjectFolderPath;
}
int FEKOResultImport::initView()
{
this->ui->FolderPathtextEdit->setText(this->FEKOProjectFolderPath);
this->ui->PreFileNametextEdit->setText(this->FEKOPreFileName);
this->ui->csvPathText->setText(this->FEKOResultCSVPath);
this->ui->NearradioButton->setChecked(this->getNearChecked());
this->ui->FarradioButton->setChecked(this->getFarChecked());
this->InitListView();
return 0;
}
int FEKOResultImport::InitListView()
{
this->ListViewmodel->clear();
this->selectListViewmodel->clear();
if (this->ui->FarradioButton->isChecked()) {
for (int i = 0; i < this->farFieldNames.size(); i++) {
QStandardItem* item = new QStandardItem(QString::QString(this->farFieldNames[i]));
this->ListViewmodel->appendRow(item);
}
}
else {
this->ui->NearradioButton->setChecked(true);
for (int i = 0; i < this->nearFieldNames.size(); i++) {
QStandardItem* item = new QStandardItem(QString::QString(this->nearFieldNames[i]));
this->ListViewmodel->appendRow(item);
}
}
this->ui->listView->setModel(this->ListViewmodel);
this->ui->SelectlistView->setModel(this->selectListViewmodel);
return 0;
}
bool FEKOResultImport::getNearChecked()
{
return this->ui->NearradioButton->isChecked();
}
bool FEKOResultImport::getFarChecked()
{
return this->ui->FarradioButton->isChecked();
}
bool FEKOResultImport::getSaveSucessfully()
{
return this->saveSucessfully;
}
QString FEKOResultImport::getSaveCsvFilePath()
{
return this->FEKOResultCSVPath;
}
// 打开文件并寻找pre文件
int FEKOResultImport::OpenPrejectFolderPath() {
//打开单个文件
QString prefileNamePath = getOpenFilePath(
this,
QString::fromUtf8(u8"选择.pre 文件"),
QString::fromUtf8(u8"pre文件 (*.pre)"));//多组扩展名用双分号";;"隔开
this->FEKOProjectFolderPath = getParantFromPath(prefileNamePath);
this->FEKOPreFileName = getFileNameFromPath(prefileNamePath);
this->farFieldNames = getFilelist(this->FEKOProjectFolderPath, ".ffe");
this->nearFieldNames = getFilelist(this->FEKOProjectFolderPath, ".efe");
// 修改显示
this->ListViewmodel->clear();
this->ui->FolderPathtextEdit->setText(this->FEKOProjectFolderPath);
this->ui->PreFileNametextEdit->setText(this->FEKOPreFileName);
if (this->ui->FarradioButton->isChecked()) {
for (int i = 0; i < this->farFieldNames.size(); i++) {
QStandardItem* item = new QStandardItem(QString::QString(this->farFieldNames[i]));
this->ListViewmodel->appendRow(item);
}
}
else {
this->ui->NearradioButton->setChecked(true);
for (int i = 0; i < this->nearFieldNames.size(); i++) {
QStandardItem* item = new QStandardItem(QString::QString(this->nearFieldNames[i]));
this->ListViewmodel->appendRow(item);
}
}
this->ui->listView->setModel(this->ListViewmodel);
qDebug()<<(u8"farField Number:"+QString::number(this->farFieldNames.size()) + "\n" + u8"NearField Number:" + QString::number(this->nearFieldNames.size()) + "\n");
return -1;
}
void FEKOResultImport::waitCMDExcute()
{
qDebug() << u8"转换程序执行结束";
if (isExists(this->FEKOResultCSVPath)) {
messageLog(u8"FEKOResult 保存成功\n路径:\t" + this->FEKOResultCSVPath, 1);
this->saveSucessfully = true;
this->p->close();
delete p;
this->p = nullptr;
}
else {
messageLog(u8"FEKOResult 保存失败\n路径:\t" + this->FEKOResultCSVPath, 1);
this->saveSucessfully = false;
}
// 触发结果时间
qDebug() << u8"准备执行转换结果编辑功能";
emit this->callbackFekoResultImport(this);
}
int FEKOResultImport::closeWindows()
{
this->close();
return 0;
}
int FEKOResultImport::NearRadioButtonSelect(bool selectTrue)
{
this->ui->NearradioButton->setChecked(selectTrue);
this->ui->FarradioButton->setChecked(!selectTrue);
this->InitListView();
return 0;
}
int FEKOResultImport::FarRadioButtonSelect(bool selectsign)
{
this->ui->NearradioButton->setChecked(!selectsign);
this->ui->FarradioButton->setChecked(selectsign);
this->InitListView();
return 0;
}
int FEKOResultImport::FEKOResultImportButtonClick()
{
if (isExists(this->FEKOResultCSVPath)) {
QFile file(this->FEKOResultCSVPath);
if (!file.open(QIODevice::ReadWrite)) {
QMessageBox::information(nullptr, u8"文件被占用", this->FEKOResultCSVPath);
return 2;
}
else {
file.close();
removeFile(this->FEKOResultCSVPath);
}
}
QString csvpramarPath = this->FEKOResultCSVPath;
csvpramarPath= csvpramarPath.replace(this->FEKOResultCSVPath.size() - 4, 4, ".params");
// 获取选择文件
std::vector<QString> selectfiles = this->getSelectFieldNames();
QString parmas_text = this->FEKOResultCSVPath +"\n";// 输出路径
for (int i = 0; i < selectfiles.size(); i++) {
parmas_text = parmas_text + selectfiles[i] + "\n";
}
if (isExists(csvpramarPath)) {
removeFile(csvpramarPath);
}
writeUTF8StringFile(csvpramarPath, (parmas_text));
// 拼接命令行
QString cmdtext = u8"";
if (this->ui->NearradioButton->isChecked()) {
cmdtext = cmdtext + u8"NearField2csvTool.exe";
}
else {
cmdtext = cmdtext + u8"FarField2csvTool.exe";
}
if (nullptr != this->p) {
this->p->close();
delete this->p;
this->p = nullptr;
}
this->p = new CMDExcuteApp();
QObject::connect(this->p, SIGNAL(callbackExcuteResult()), this, SLOT(waitCMDExcute()));
this->p->setAttribute(Qt::WA_DeleteOnClose);;
this->p->show();
this->p->excuteCmd(cmdtext, csvpramarPath);
return 0;
}
int FEKOResultImport::AddFekoResultClick()
{
QModelIndexList sourcelist=this->ui->listView->selectionModel()->selectedIndexes();
std::vector<int> deleteRowids(0);
for (int i = 0; i < sourcelist.count(); i++) {
deleteRowids.push_back(sourcelist[i].row());
qDebug() << ("deletaRowids add :"+QString::number(sourcelist[i].row())+"\n");
}
std::sort(deleteRowids.begin(), deleteRowids.end());
for (int i = deleteRowids.size() - 1; i >= 0; i--) {
int select_Idx = deleteRowids[i];
QList<QStandardItem*> listItem = this->ListViewmodel->takeRow(select_Idx);
int nRightCount = this->selectListViewmodel->rowCount();
this->selectListViewmodel->insertRow(nRightCount, listItem);
qDebug() << ("selectListViewmodel insertRow : "+ QString::number(select_Idx) +"-->" + QString::number(nRightCount) + "\n");
}
this->ui->listView->setModel(this->ListViewmodel);
this->ui->SelectlistView->setModel(this->selectListViewmodel);
return 0;
}
int FEKOResultImport::DeleteFekoResultClick()
{
QModelIndexList sourcelist = this->ui->SelectlistView->selectionModel()->selectedIndexes();
std::vector<int> deleteRowids(0);
for (int i = 0; i < sourcelist.count(); i++) {
deleteRowids.push_back(sourcelist[i].row());
}
std::sort(deleteRowids.begin(), deleteRowids.end());
for (int i = deleteRowids.size() - 1; i >= 0; i--) {
int select_Idx = deleteRowids[i];
QList<QStandardItem*> listItem = this->selectListViewmodel->takeRow(select_Idx);
int nRightCount = this->ListViewmodel->rowCount();
this->ListViewmodel->insertRow(nRightCount, listItem);
qDebug() << ("ListViewmodel insertRow : " + QString::number(select_Idx) + "-->" + QString::number(nRightCount) + "\n");
}
this->ui->listView->setModel(this->ListViewmodel);
this->ui->SelectlistView->setModel(this->selectListViewmodel);
return 0;
}
int FEKOResultImport::SaveCsvFilePath()
{
//打开单个文件
QString csvfileNamePath = getSaveFilePath(
this,
QString::fromUtf8(u8"保存FEKO解析结果 csv "),
QString::fromUtf8(u8"csv文件 (*.csv)"));//多组扩展名用双分号";;"隔开
this->FEKOResultCSVPath = csvfileNamePath;
this->ui->csvPathText->setPlainText(csvfileNamePath);
return 0;
}

View File

@ -0,0 +1,89 @@
#pragma once
#ifndef FEKORESULTIMPORT_H
#define FEKORESULTIMPORT_H
#include "WBFZExchangePluginAPI.h"
#include "WBFZExchangePluginAPI.h"
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QApplication>
#include "TaskTreeClass.h"
#include "CMDExcuteApp.h"
#include <QStandardItemModel>
#include <QStringListModel>
#include <stdlib.h>
#include <stdio.h>
#include <vector>
#include <QObject>
class QObject;
class QMainWindow;
namespace Ui{
class FEKOResultImportWindow;
}
class FEKOResultImport : public QMainWindow
{
Q_OBJECT
public:
QString FEKOProjectFolderPath;
QString FEKOPreFileName;
std::vector<QString> nearFieldNames;
std::vector<QString> farFieldNames;
QString FEKOResultCSVPath;
private :
QStandardItemModel* ListViewmodel;
QStandardItemModel* selectListViewmodel;
bool saveSucessfully;
CMDExcuteApp* p;
public:
FEKOResultImport(QWidget* parent = nullptr);
~FEKOResultImport();
QString getFEKOPreFileName();
int setFEKOPreFileName(QString preFileName);
int setNearFieldNames(std::vector<QString> nearFieldNames);
std::vector<QString> getNearFieldNames();
int setFarFieldNames(std::vector<QString> farFieldNames);
std::vector<QString> getFarFieldNames();
int setSelectFieldNames(std::vector<QString> SelectFieldNames,bool nearOrfar);
std::vector<QString> getSelectFieldNames();
int setFEKOResultCSVPath(QString csvPath);
QString getFEKOResultCSVPath();
int setFEKOPreProjectFolderPath(QString FolderPath);
QString getFEKOPreProjectFolderPath();
int initView();
int InitListView();
bool getNearChecked();
bool getFarChecked();
bool getSaveSucessfully();
QString getSaveCsvFilePath();
private:
Ui::FEKOResultImportWindow* ui;
signals:
void callbackFekoResultImport(FEKOResultImport* obj);
public slots: // 信号(或者事件)
int OpenPrejectFolderPath();
int closeWindows();
int NearRadioButtonSelect(bool);
int FarRadioButtonSelect(bool);
int FEKOResultImportButtonClick(); // 执行导入
int AddFekoResultClick();
int DeleteFekoResultClick();
int SaveCsvFilePath();
void waitCMDExcute(); // 导入完成
};
#endif

View File

@ -0,0 +1,425 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FEKOResultImportWindow</class>
<widget class="QMainWindow" name="FEKOResultImportWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>621</width>
<height>458</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>621</width>
<height>331</height>
</size>
</property>
<property name="windowTitle">
<string>FEKO近场文件导入</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,0">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item>
<widget class="QLabel" name="label">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>30</height>
</size>
</property>
<property name="text">
<string>pre工程路径</string>
</property>
</widget>
</item>
<item>
<widget class="QTextEdit" name="FolderPathtextEdit">
<property name="enabled">
<bool>false</bool>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>30</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="OKpushButton">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>30</height>
</size>
</property>
<property name="text">
<string>打开</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label_3">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>30</height>
</size>
</property>
<property name="text">
<string>FEKO结果输出地址</string>
</property>
</widget>
</item>
<item>
<widget class="QTextEdit" name="csvPathText">
<property name="enabled">
<bool>false</bool>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>30</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>30</height>
</size>
</property>
<property name="text">
<string>选择保存文件</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_2">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>30</height>
</size>
</property>
<property name="text">
<string>.pre文件名</string>
</property>
</widget>
</item>
<item>
<widget class="QTextEdit" name="PreFileNametextEdit">
<property name="enabled">
<bool>false</bool>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>30</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="NearradioButton">
<property name="text">
<string>NearField</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="FarradioButton">
<property name="text">
<string>FarField</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QListView" name="listView">
<property name="minimumSize">
<size>
<width>0</width>
<height>200</height>
</size>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::MultiSelection</enum>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="AddpushButton">
<property name="text">
<string>&gt;&gt;</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="deletepushButton">
<property name="text">
<string>&lt;&lt;</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QListView" name="SelectlistView">
<property name="minimumSize">
<size>
<width>0</width>
<height>200</height>
</size>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::MultiSelection</enum>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="ImportpushButton">
<property name="text">
<string>导入FEKO结果</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="CancelpushButton">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>30</height>
</size>
</property>
<property name="text">
<string>取消</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections>
<connection>
<sender>OKpushButton</sender>
<signal>clicked()</signal>
<receiver>FEKOResultImportWindow</receiver>
<slot>OpenPrejectFolderPath()</slot>
<hints>
<hint type="sourcelabel">
<x>802</x>
<y>52</y>
</hint>
<hint type="destinationlabel">
<x>519</x>
<y>99</y>
</hint>
</hints>
</connection>
<connection>
<sender>NearradioButton</sender>
<signal>clicked(bool)</signal>
<receiver>FEKOResultImportWindow</receiver>
<slot>NearRadioButtonSelect(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>728</x>
<y>175</y>
</hint>
<hint type="destinationlabel">
<x>471</x>
<y>112</y>
</hint>
</hints>
</connection>
<connection>
<sender>FarradioButton</sender>
<signal>clicked(bool)</signal>
<receiver>FEKOResultImportWindow</receiver>
<slot>FarRadioButtonSelect(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>802</x>
<y>175</y>
</hint>
<hint type="destinationlabel">
<x>504</x>
<y>86</y>
</hint>
</hints>
</connection>
<connection>
<sender>ImportpushButton</sender>
<signal>clicked()</signal>
<receiver>FEKOResultImportWindow</receiver>
<slot>FEKOResultImportButtonClick()</slot>
<hints>
<hint type="sourcelabel">
<x>721</x>
<y>435</y>
</hint>
<hint type="destinationlabel">
<x>514</x>
<y>158</y>
</hint>
</hints>
</connection>
<connection>
<sender>deletepushButton</sender>
<signal>clicked()</signal>
<receiver>FEKOResultImportWindow</receiver>
<slot>DeleteFekoResultClick()</slot>
<hints>
<hint type="sourcelabel">
<x>443</x>
<y>354</y>
</hint>
<hint type="destinationlabel">
<x>507</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>AddpushButton</sender>
<signal>clicked()</signal>
<receiver>FEKOResultImportWindow</receiver>
<slot>AddFekoResultClick()</slot>
<hints>
<hint type="sourcelabel">
<x>443</x>
<y>277</y>
</hint>
<hint type="destinationlabel">
<x>542</x>
<y>212</y>
</hint>
</hints>
</connection>
<connection>
<sender>CancelpushButton</sender>
<signal>clicked()</signal>
<receiver>FEKOResultImportWindow</receiver>
<slot>closeWindows()</slot>
<hints>
<hint type="sourcelabel">
<x>802</x>
<y>435</y>
</hint>
<hint type="destinationlabel">
<x>547</x>
<y>48</y>
</hint>
</hints>
</connection>
<connection>
<sender>pushButton</sender>
<signal>clicked()</signal>
<receiver>FEKOResultImportWindow</receiver>
<slot>SaveCsvFilePath()</slot>
<hints>
<hint type="sourcelabel">
<x>757</x>
<y>106</y>
</hint>
<hint type="destinationlabel">
<x>767</x>
<y>136</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>OpenPrejectFolderPath()</slot>
<slot>closeWindows()</slot>
<slot>NearRadioButtonSelect(bool)</slot>
<slot>FarRadioButtonSelect(bool)</slot>
<slot>FEKOResultImportButtonClick()</slot>
<slot>AddFekoResultClick()</slot>
<slot>DeleteFekoResultClick()</slot>
<slot>SaveCsvFilePath()</slot>
</slots>
</ui>

View File

@ -0,0 +1,226 @@
#include "TaskTreeClass.h"
#include <iostream>
#include <memory>
#include <boost/filesystem.hpp>
#include <QStandardItemModel>
#include <QStringListModel>
#include <QMessageBox>
#include <unordered_set>
#include <QProcess>
#include <QDebug>
#include <QtCore>
#include <QtXml>
#include <QDomDocument>
TaskNode::TaskNode(QWidget* parent) :QCheckBox(parent)
{
this->status = TaskStatusEnum::wait;
this->setText("TaskNode");
this->description = "TaskNode";
this->TaskXmlPath = "";
this->setContextMenuPolicy(Qt::CustomContextMenu);
this->ContentListContextMenu = new QMenu(this); // 表格控件的右键菜单
QObject::connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(ShowContentListContextMenu(QPoint)));
//// 加载数据
//QAction* action_Node_ExcuteTask_Action = this->ContentListContextMenu->addAction(u8"加载");
//QObject::connect(action_Node_ExcuteTask_Action, SIGNAL(triggered()), this, SLOT(ExcuteTask()));
// 重命名
QAction* action_rename=this->ContentListContextMenu->addAction(u8"重命名");
QObject::connect(action_rename, SIGNAL(triggered()), this, SLOT(renameNode()));
// 移除
QAction* action_remove = this->ContentListContextMenu->addAction(u8"移除");
QObject::connect(action_remove, SIGNAL(triggered()), this, SLOT(deleteNode()));
}
TaskNode::~TaskNode()
{
this->Nodelist.clear();
}
QString TaskNode::getName()
{
return this->text();
}
int TaskNode::setName(QString name)
{
this->setText(name);
return 0;
}
QString TaskNode::getTaskName()
{
return this->text();
}
int TaskNode::loadXmlDocument(QString xmlFilePath, QDomDocument& doc)
{
QFile file(xmlFilePath.toUtf8().constData()); //相对路径、绝对路径、资源路径都可以
if (!file.open(QFile::ReadOnly)) //可以用QIODeviceTruncate表示清空原来的内容
{
QString tiptext = "File isn't opened in readonly mode ,file path "+ xmlFilePath;
qDebug() << tiptext.toUtf8().constData();
return 2;
}
QTextStream stream(&file);
QTextCodec* codec = QTextCodec::codecForName(u8"UTF-8");
stream.setCodec(codec);
QString content = stream.readAll();
file.close();
if (!doc.setContent(content)) // 关联文件流
{
QString tiptext = u8"File isn't readed in xml format ,file path " + xmlFilePath;
qDebug() << tiptext.toUtf8().constData();
file.close();
return 3;
}
file.close(); // 文件读取结束
return -1;
}
int TaskNode::writeXmlDocument(QDomDocument& doc)
{
QFile wfile(this->TaskXmlPath.toUtf8().constData()); // 保存 XML 文件
if (wfile.open(QFile::ReadWrite | QFile::Text))
{
QTextStream out(&wfile);
doc.save(out, QDomNode::EncodingFromDocument);
wfile.close();
}
return -1;
}
int TaskNode::loadXmlFile(QString xmlFilePath)
{
return 0;
}
int TaskNode::saveXmlFile()
{
return 0;
}
int TaskNode::addNode(std::shared_ptr<TaskNode> n)
{
this->Nodelist.push_back(n);
return 0;
}
std::shared_ptr<TaskNode> TaskNode::getNode(int nid)
{
if (nid < this->Nodelist.size()) {
return this->Nodelist[nid];
}
return std::shared_ptr<TaskNode>(nullptr);
}
std::shared_ptr<TaskNode> TaskNode::getNode(QString nodename)
{
for (int i = 0; i < this->Nodelist.size(); i++) {
if (strcmp(nodename.toUtf8().constData(), this->Nodelist[i]->getName().toUtf8().constData()) == 0) {
return this->Nodelist[i];
}
}
return std::shared_ptr<TaskNode>(nullptr);
}
std::shared_ptr<TaskNode> TaskNode::removeAt(int nid)
{
if (nid < this->Nodelist.size()) {
std::shared_ptr<TaskNode> result = this->Nodelist[nid];
this->Nodelist.erase(this->Nodelist.begin()+nid); // 删除 nid
return result;
}
return std::shared_ptr<TaskNode>(nullptr);
}
std::shared_ptr<TaskNode> TaskNode::removeByName(QString nodename)
{
for (int i = 0; i < this->Nodelist.size(); i++) {
if (strcmp(nodename.toUtf8().constData(), this->Nodelist[i]->getName().toUtf8().constData()) == 0) {
std::shared_ptr<TaskNode> result = this->Nodelist[i];
this->Nodelist.erase(this->Nodelist.begin() + i); // 删除 nid
return result;
}
}
return std::shared_ptr<TaskNode>(nullptr);
}
TaskStatusEnum TaskNode::getStatus()
{
return this->status;
// return TaskStatusEnum();
}
int TaskNode::setStatus(TaskStatusEnum taskstatus)
{
this->status = taskstatus;
return 0;
}
void TaskNode::renameNode()
{
QString newName=QInputDialog::getText(this, u8"重命名", u8"请输入新的名称", QLineEdit::Normal, this->text());
emit this->renameNode(this->text(), newName);
}
void TaskNode::deleteNode()
{
this->FinishTask();
emit this->deleteNode(this->text());
}
void TaskNode::copyNewNode()
{
emit this->copyNewNode(this->CopyToNew());
}
void TaskNode::ShowContentListContextMenu(QPoint p)
{
this->ContentListContextMenu->exec(QCursor::pos());
}
int TaskNode::ExcuteTask()
{
this->status = TaskStatusEnum::excuting;
return 0;
}
int TaskNode::FinishTask()
{
return 0;
}
TaskNode* TaskNode::CopyToNew()
{
return nullptr;
}
int TaskNode::preView()
{
return 0;
}
int TaskTreeClass::loadXmlFile(QString xmlFilePath)
{
return 0;
}
int TaskTreeClass::saveXmlFile()
{
return 0;
}

View File

@ -0,0 +1,105 @@
#pragma once
/*
* 使
*
*
*
* 仿
*
* 1.
* 2.
* 3.
* 4. --
* 5.
* 仿
* <feko>
* <FEKO>
*
*
***/
#ifndef TASKTREECLASS_H
#define TASKTREECLASS_H
#include "WBFZExchangePluginAPI.h"
#include "AllHead.h"
#include <memory>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <string>
#include <vector>
#include <rapidxml/rapidxml.hpp>
#include <rapidxml/rapidxml_utils.hpp> //rapidxml::file
#include <rapidxml/rapidxml_print.hpp> //rapidxml::print
#include <QObject>
#include <QDomDocument>
class QObject;
class QCheckBox;
class TaskNode :public QCheckBox
{
Q_OBJECT // 每个子类必须使用这个宏
public:
QString TaskXmlPath;
public:
QMenu* ContentListContextMenu;
QString description; // 描述
TaskStatusEnum status;
std::vector<std::shared_ptr<TaskNode>> Nodelist;
public:
TaskNode(QWidget* parent = nullptr);
~TaskNode();
public:
QString getName();
int setName(QString anme);
QString getTaskName();
virtual int ExcuteTask(); // 任务执行操作
virtual int FinishTask();
virtual TaskNode* CopyToNew();
int loadXmlDocument(QString xmlfilepath, QDomDocument& doc);
int writeXmlDocument(QDomDocument& doc);
virtual int preView(); // 预览信息
virtual int loadXmlFile(QString xmlFilePath); // 加载任务文件,必须重写
virtual int saveXmlFile(); // 保存任务文件,必须重写
virtual int addNode(std::shared_ptr<TaskNode> n);
virtual std::shared_ptr<TaskNode> getNode(int nid);
virtual std::shared_ptr<TaskNode> getNode(QString nodename);
virtual std::shared_ptr<TaskNode> removeAt(int nid);
virtual std::shared_ptr<TaskNode> removeByName(QString nodename);
virtual TaskStatusEnum getStatus();
virtual int setStatus(TaskStatusEnum taskstatus);
public slots:
void ShowContentListContextMenu(QPoint p);
void renameNode();
void deleteNode();
void copyNewNode();
signals:
void renameNode(QString oldname,QString newname);
void deleteNode(QString name);
void copyNewNode(TaskNode* sendernode);
};
/// <summary>
/// 任务管理地址
/// </summary>
class TaskTreeClass:public QObject
{
Q_OBJECT
public:
QString TaskName;
std::vector<TaskNode> tasknodes;
public:
virtual int loadXmlFile(QString xmlFilePath); // 加载任务文件,必须重写
virtual int saveXmlFile(); // 保存任务文件,必须重写
};
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,43 @@
[General]
CoordinateSystemName="Spheric(R,theta,phi)"
description=R半径theta 入射角phi 方位角
A1="9.3,90,0"
A2="9.3,90,180"
S1="9.3,45,14"
S3="9.3,45,45"
S5="9.3,45,60"
S8="9.3,45,120"
S10="9.3,45,135"
S12="9.3,45,166"
F1="9.3,75,15"
F2="9.3,75,30"
F3="9.3,75,45"
F4="9.3,75,60"
F5="9.3,75,75"
F6="9.3,75,86"
F7="9.3,75,94"
F8="9.3,75,105"
F9="9.3,75,120"
F10="9.3,75,135"
F11="9.3,75,151"
F12="9.3,75,165"
ant_A1=A1_ant.ffe
ant_A2=A2_ant.ffe
ant_S1=S1_ant.ffe
ant_S3=S3_ant.ffe
ant_S5=S5_ant.ffe
ant_S8=S8_ant.ffe
ant_S10=S10_ant.ffe
ant_S12=S12_ant.ffe
ant_F1=F1_ant.ffe
ant_F2=F2_ant.ffe
ant_F3=F3_ant.ffe
ant_F4=F4_ant.ffe
ant_F5=F5_ant.ffe
ant_F6=F6_ant.ffe
ant_F7=F7_ant.ffe
ant_F8=F8_ant.ffe
ant_F9=F9_ant.ffe
ant_F10=F10_ant.ffe
ant_F11=F11_ant.ffe
ant_F12=F12_ant.ffe

View File

@ -0,0 +1,207 @@
#-----------------------------------------------------------------------------
# mesh
# -> mesh
# mesh->-> -> mesh
#
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
#
#-----------------------------------------------------------------------------
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
# boost
include_directories(D:/vcpkg/installed/x64-windows/include)
# pcl
include_directories(C:/PCL/3rdParty/FLANN/include)
include_directories(C:/PCL/3rdParty/VTK/include/vtk-9.3)
include_directories(C:/PCL/include/pcl-1.14)
# qt5
include_directories(C:/Qt/5.15.2/msvc2019_64/include/QtQml)
# json
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../json)
# qscintilla2
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../qscintilla2)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../qscintilla2/lexers)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../qscintilla2/include)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../qscintilla2/lexlib)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../qscintilla2/Qt4Qt5)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../qscintilla2/src)
# qcustomplot
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../qcustomplot)
# qhexedit
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../qhexedit)
#
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/SqliteDBProcess/src)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/modelProcess)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/EchoShowProcess)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/OCCViewer)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/PointCloudProcess)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/SharedModuleLib)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/TableProcess)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/TaskXml)
#-----------------------------------------------------------------------------
#
#-----------------------------------------------------------------------------
# pcl
link_directories("C:/PCL/3rdParty/FLANN/lib")
link_directories("C:/VTK/lib")
link_directories("C:/PCL/lib")
#-----------------------------------------------------------------------------
# include
#-----------------------------------------------------------------------------
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
#-----------------------------------------------------------------------------
#
#-----------------------------------------------------------------------------
set(_qrc "${CMAKE_CURRENT_SOURCE_DIR}/../qrc/WBCLFZSystemModule.qrc")
set(_lang "${CMAKE_CURRENT_SOURCE_DIR}/../qrc/translations.qrc")
qt5_add_resources(_resource ${_qrc} ${_lang})
#-----------------------------------------------------------------------------
#
#-----------------------------------------------------------------------------
file(GLOB _ui "*.ui")
file(GLOB _header "*.h" "*.hpp")
file(GLOB _source "*.cpp")
qt5_wrap_ui(_interface ${_ui})
message("_source ${_source}")
#-----------------------------------------------------------------------------
#
#-----------------------------------------------------------------------------
add_library(PluginWBFZExchangePlugin
${_resource}
${_interface}
${_header}
${_source}
)
#-----------------------------------------------------------------------------
#
#-----------------------------------------------------------------------------
# pcl
link_directories("C:/PCL/3rdParty/FLANN/lib")
link_directories("C:/VTK/lib")
link_directories("C:/PCL/lib")
find_package(Qt5 REQUIRED COMPONENTS Core Quick Sql Core Xml Opengl Gui Svg Xmlpatterns Uitools Widgets Qml Printsupport Sensors Quickwidgets Quick Concurrent Openglextensions Charts Datavisualization)
find_package(PCL )
# boost
include_directories(D:/vcpkg/installed/x64-windows/include)
# pcl
include_directories(C:/PCL/3rdParty/FLANN/include)
include_directories(C:/PCL/3rdParty/VTK/include/vtk-9.3)
include_directories(C:/PCL/include/pcl-1.14)
include_directories(${PCL_INCLUDE_DIRS})
link_directories(${PCL_LIBRARY_DIRS})
add_definitions(${PCL_DEFINITIONS})
#-----------------------------------------------------------------------------
#
#-----------------------------------------------------------------------------
target_compile_definitions(PluginWBFZExchangePlugin PRIVATE "NOMINMAX") # vc++ min max pcl std::minpcl::max
target_compile_definitions(PluginWBFZExchangePlugin PRIVATE "WBFZ_API")
target_compile_definitions(PluginWBFZExchangePlugin PRIVATE "WBCLFZSYSTEMMODULE_API")
target_compile_definitions(PluginWBFZExchangePlugin PRIVATE "LAMPTOOL_API")
#-----------------------------------------------------------------------------
# Qt
#-----------------------------------------------------------------------------
get_target_property(_qmake_executable Qt5::qmake IMPORTED_LOCATION)
get_filename_component(_qt_bin_dir "${_qmake_executable}" DIRECTORY)
list(APPEND _depend_library
qcustomplot qhexedit qscintilla2 Common PointCloudOperator Settings PythonModule DataProperty MeshData Material Geometry BCBase ConfigOptions SelfDefObject ModelData ModuleBase PluginManager GeometryCommand GeometryWidgets IO MainWidgets MainWindow
)
list(APPEND _runtimes_libraries
LAMPCAE::CGNS Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Xml
)
list(APPEND _runtimes_libraries
Qt5::Network Qt5::Core Qt5::Gui Qt5::Widgets Qt5::DBus Qt5::Core Qt5::Xml Qt5::OpenGL Qt5::Gui Qt5::Svg Qt5::Widgets Qt5::Qml Qt5::DataVisualization Qt5::Charts Qt5::PrintSupport Qt5::Test
)
list(APPEND _runtimes_libraries
OpenCASCADE::TKOpenGl OpenCASCADE::TKOffset OpenCASCADE::TKSTL OpenCASCADE::Freetype OpenCASCADE::TKBO OpenCASCADE::TKBRep OpenCASCADE::TKBool OpenCASCADE::TKCAF OpenCASCADE::TKCDF OpenCASCADE::TKG2d OpenCASCADE::TKG3d OpenCASCADE::TKGeomAlgo OpenCASCADE::TKGeomBase OpenCASCADE::TKHLR OpenCASCADE::TKIGES OpenCASCADE::TKLCAF OpenCASCADE::TKMath OpenCASCADE::TKMesh OpenCASCADE::TKPrim OpenCASCADE::TKSTEP OpenCASCADE::TKSTEP209 OpenCASCADE::TKSTEPAttr OpenCASCADE::TKSTEPBase OpenCASCADE::TKService OpenCASCADE::TKShHealing OpenCASCADE::TKTopAlgo OpenCASCADE::TKV3d OpenCASCADE::TKVCAF OpenCASCADE::TKXCAF OpenCASCADE::TKXDEIGES OpenCASCADE::TKXSBase OpenCASCADE::TKernel Qt5::Widgets Qt5::Xml VTK::CommonColor VTK::CommonComputationalGeometry VTK::CommonCore VTK::CommonDataModel VTK::CommonExecutionModel VTK::CommonMath VTK::CommonMisc VTK::CommonSystem VTK::CommonTransforms VTK::FiltersCore VTK::FiltersExtraction VTK::FiltersGeneral VTK::FiltersGeometry VTK::FiltersSources VTK::FiltersStatistics VTK::IOCore VTK::IOLegacy VTK::IOXML VTK::IOXMLParser VTK::ImagingCore VTK::ImagingFourier VTK::ImagingMath VTK::InteractionStyle VTK::ParallelCore VTK::ParallelDIY VTK::RenderingCore VTK::RenderingFreeType VTK::RenderingOpenGL2 VTK::RenderingUI VTK::RenderingVolume VTK::RenderingVolumeOpenGL2 VTK::doubleconversion VTK::expat VTK::freetype VTK::glew VTK::lz4 VTK::lzma VTK::sys VTK::zlib VTK::IOGeometry
)
list(APPEND _runtimes_libraries
VTK::CommonColor VTK::CommonComputationalGeometry VTK::CommonCore VTK::CommonDataModel VTK::CommonExecutionModel VTK::CommonMath VTK::CommonMisc VTK::CommonSystem VTK::CommonTransforms VTK::DICOMParser VTK::FiltersCore VTK::FiltersGeneral VTK::FiltersGeometry VTK::FiltersHybrid VTK::FiltersModeling VTK::FiltersSources VTK::IOCore VTK::IOChemistry VTK::IOGeometry VTK::IOImage VTK::IOLegacy VTK::ImagingCore VTK::ImagingSources VTK::RenderingCore VTK::RenderingLOD VTK::doubleconversion VTK::jpeg VTK::jsoncpp VTK::lz4 VTK::lzma VTK::metaio VTK::png VTK::pugixml VTK::sys VTK::tiff VTK::zlib
)
target_include_directories(PluginWBFZExchangePlugin PRIVATE ${Qwt_INCLUDE_DIRS})
#-----------------------------------------------------------------------------
#
#-----------------------------------------------------------------------------
target_link_libraries(PluginWBFZExchangePlugin PRIVATE
${_runtimes_libraries}
${_depend_library}
${PCL_LIBRARIES}
unofficial::sqlite3::sqlite3
GDAL::GDAL
FFTW3::fftw3
GSL::gsl GSL::gslcblas
)
#-----------------------------------------------------------------------------
#
#-----------------------------------------------------------------------------
add_dependencies(PluginWBFZExchangePlugin ${_depend_library})
#-----------------------------------------------------------------------------
#
#-----------------------------------------------------------------------------
set(LAMPCAE_PluginWBFZExchangePlugin_Runtimes_Libraries ${_runtimes_libraries} PARENT_SCOPE)
#-----------------------------------------------------------------------------
#
#-----------------------------------------------------------------------------
set_target_properties(PluginWBFZExchangePlugin
PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY_RELEASE $<TARGET_FILE_DIR:${PROJECT_NAME}>/plugins
LIBRARY_OUTPUT_DIRECTORY_RELEASE $<TARGET_FILE_DIR:${PROJECT_NAME}>/plugins
RUNTIME_OUTPUT_DIRECTORY_RELEASE $<TARGET_FILE_DIR:${PROJECT_NAME}>/plugins
ARCHIVE_OUTPUT_DIRECTORY_DEBUG $<TARGET_FILE_DIR:${PROJECT_NAME}>/plugins
LIBRARY_OUTPUT_DIRECTORY_DEBUG $<TARGET_FILE_DIR:${PROJECT_NAME}>/plugins
RUNTIME_OUTPUT_DIRECTORY_DEBUG $<TARGET_FILE_DIR:${PROJECT_NAME}>/plugins
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -0,0 +1,391 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
inkscape:export-ydpi="96"
inkscape:export-xdpi="96"
inkscape:export-filename="./logo.png"
version="1.1"
inkscape:output_extension="org.inkscape.output.svg.inkscape"
width="256"
height="256"
id="svg1328"
sodipodi:version="0.32"
inkscape:version="1.0 (unknown)"
sodipodi:docname="logo.svg">
<title
id="title917">logo</title>
<defs
id="defs3">
<linearGradient
id="linearGradient6932">
<stop
id="stop6934"
offset="0"
style="stop-color:#a2a2a2;stop-opacity:1;" />
<stop
id="stop6936"
offset="1"
style="stop-color:#4e4e4e;stop-opacity:1;" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient6924">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop6926" />
<stop
style="stop-color:#ffffff;stop-opacity:0;"
offset="1"
id="stop6928" />
</linearGradient>
<linearGradient
id="linearGradient4162">
<stop
style="stop-color:#ababab;stop-opacity:1;"
offset="0"
id="stop4164" />
<stop
id="stop4170"
offset="0.19415782"
style="stop-color:#f6f6f6;stop-opacity:1;" />
<stop
style="stop-color:#b0b0b0;stop-opacity:1;"
offset="0.39749253"
id="stop4172" />
<stop
style="stop-color:#585858;stop-opacity:1;"
offset="1"
id="stop4166" />
</linearGradient>
<linearGradient
id="linearGradient4162-2">
<stop
style="stop-color:#ababab;stop-opacity:1;"
offset="0"
id="stop4164-3" />
<stop
id="stop4170-1"
offset="0.19415782"
style="stop-color:#f6f6f6;stop-opacity:1;" />
<stop
style="stop-color:#b0b0b0;stop-opacity:1;"
offset="0.39749253"
id="stop4172-6" />
<stop
style="stop-color:#585858;stop-opacity:1;"
offset="1"
id="stop4166-8" />
</linearGradient>
<linearGradient
id="linearGradient6932-2">
<stop
id="stop6934-7"
offset="0"
style="stop-color:#a2a2a2;stop-opacity:1;" />
<stop
id="stop6936-7"
offset="1"
style="stop-color:#4e4e4e;stop-opacity:1;" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient6924-2">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop6926-6" />
<stop
style="stop-color:#ffffff;stop-opacity:0;"
offset="1"
id="stop6928-0" />
</linearGradient>
<linearGradient
id="linearGradient4162-3">
<stop
style="stop-color:#ababab;stop-opacity:1;"
offset="0"
id="stop4164-6" />
<stop
id="stop4170-2"
offset="0.19415782"
style="stop-color:#f6f6f6;stop-opacity:1;" />
<stop
style="stop-color:#b0b0b0;stop-opacity:1;"
offset="0.39749253"
id="stop4172-4" />
<stop
style="stop-color:#585858;stop-opacity:1;"
offset="1"
id="stop4166-5" />
</linearGradient>
<linearGradient
id="linearGradient6932-1">
<stop
id="stop6934-2"
offset="0"
style="stop-color:#a2a2a2;stop-opacity:1;" />
<stop
id="stop6936-1"
offset="1"
style="stop-color:#4e4e4e;stop-opacity:1;" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient6924-9">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop6926-8" />
<stop
style="stop-color:#ffffff;stop-opacity:0;"
offset="1"
id="stop6928-2" />
</linearGradient>
<linearGradient
y2="17.511776"
x2="38.013393"
y1="17.511776"
x1="10.116071"
gradientTransform="translate(-1.142857e-6,-1.714284)"
gradientUnits="userSpaceOnUse"
id="linearGradient3933"
xlink:href="#linearGradient4162-3"
inkscape:collect="always" />
<linearGradient
y2="13.284962"
x2="24"
y1="15.149456"
x1="24"
gradientUnits="userSpaceOnUse"
id="linearGradient3935"
xlink:href="#linearGradient6932-1"
inkscape:collect="always" />
<linearGradient
y2="24.0401"
x2="16.189775"
y1="19.5"
x1="16.07143"
gradientTransform="translate(-0.142857,0.928571)"
gradientUnits="userSpaceOnUse"
id="linearGradient3937"
xlink:href="#linearGradient6924-9"
inkscape:collect="always" />
<linearGradient
y2="17.511776"
x2="38.013393"
y1="17.511776"
x1="10.116071"
gradientTransform="translate(-1.142857e-6,-1.714284)"
gradientUnits="userSpaceOnUse"
id="linearGradient3939"
xlink:href="#linearGradient4162-2"
inkscape:collect="always" />
<linearGradient
y2="13.284962"
x2="24"
y1="15.149456"
x1="24"
gradientUnits="userSpaceOnUse"
id="linearGradient3941"
xlink:href="#linearGradient6932-2"
inkscape:collect="always" />
<linearGradient
y2="24.0401"
x2="16.189775"
y1="19.5"
x1="16.07143"
gradientTransform="translate(-0.142857,0.928571)"
gradientUnits="userSpaceOnUse"
id="linearGradient3943"
xlink:href="#linearGradient6924-2"
inkscape:collect="always" />
<linearGradient
y2="17.511776"
x2="38.013393"
y1="17.511776"
x1="10.116071"
gradientTransform="translate(-1.142857e-6,-1.714284)"
gradientUnits="userSpaceOnUse"
id="linearGradient3945"
xlink:href="#linearGradient4162"
inkscape:collect="always" />
<linearGradient
y2="13.284962"
x2="24"
y1="15.149456"
x1="24"
gradientUnits="userSpaceOnUse"
id="linearGradient3947"
xlink:href="#linearGradient6932"
inkscape:collect="always" />
<linearGradient
y2="24.0401"
x2="16.189775"
y1="19.5"
x1="16.07143"
gradientTransform="translate(-0.142857,0.928571)"
gradientUnits="userSpaceOnUse"
id="linearGradient3949"
xlink:href="#linearGradient6924"
inkscape:collect="always" />
</defs>
<sodipodi:namedview
inkscape:document-rotation="0"
fit-margin-bottom="0.5"
fit-margin-right="1.9"
fit-margin-left="1.9"
fit-margin-top="0.22"
inkscape:window-maximized="1"
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="0.25490196"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.75"
inkscape:cx="124.25477"
inkscape:cy="48.766398"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:showpageshadow="false"
fill="#cc0000"
inkscape:window-width="1680"
inkscape:window-height="1005"
inkscape:window-x="0"
inkscape:window-y="23"
stroke="#a40000" />
<metadata
id="metadata4">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>logo</dc:title>
<dc:creator>
<cc:Agent>
<dc:title></dc:title>
</cc:Agent>
</dc:creator>
<dc:source>https://sqlitebrowser.org</dc:source>
<dc:subject>
<rdf:Bag />
</dc:subject>
<cc:license
rdf:resource="http://creativecommons.org/licenses/by/4.0/" />
<dc:description>DB Browser for SQLite Logo</dc:description>
<dc:date>2020Jun05</dc:date>
<dc:rights>
<cc:Agent>
<dc:title>GPL3+</dc:title>
</cc:Agent>
</dc:rights>
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by/4.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Notice" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Attribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
</cc:License>
</rdf:RDF>
</metadata>
<g
transform="translate(-4.9016066,212.3059)"
id="layer1"
inkscape:label="Battery"
inkscape:groupmode="layer">
<g
transform="matrix(8.5968764,0,0,6.3004722,-74.054128,-252.36013)"
id="g3916">
<g
transform="translate(0.01853025,20.001561)"
id="g3795-5">
<path
inkscape:connector-curvature="0"
sodipodi:nodetypes="zsszssz"
id="path4152-2"
d="m 23.941292,8.0370051 c -7.625145,0 -13.825221,2.1304449 -13.825222,5.4751769 v 6.557844 c 0,3.344734 6.200076,5.647898 13.825222,5.647898 7.625146,0 14.072102,-2.303164 14.0721,-5.647898 v -6.557844 c 0,-3.344733 -6.446955,-5.4751769 -14.0721,-5.4751769 z"
style="color:#000000;display:block;overflow:visible;visibility:visible;fill:url(#linearGradient3933);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.769;marker:none" />
<ellipse
style="color:#000000;display:block;overflow:visible;visibility:visible;fill:#c3c3c3;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient3935);stroke-width:1.00493;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none"
id="path3364-9"
transform="matrix(1.037291,0,0,0.954619,-0.894979,-0.0757206)"
cx="24"
cy="14.071428"
rx="12.857142"
ry="5.5" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:block;overflow:visible;visibility:visible;opacity:0.493671;fill:url(#linearGradient3937);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none"
d="m 13.642858,17.999999 0.256127,5.876067 4.672444,1.083684 -0.142857,-5.602608 c 0,0 2.071428,-0.03993 5.428571,-0.254216 -5.215743,-0.233073 -11.183373,-2.725109 -13.214285,-4.179 1.416899,2.092176 3,3.076073 3,3.076073 z"
id="path6922-9"
sodipodi:nodetypes="ccccccc" />
</g>
<g
transform="translate(-0.00126425,9.9604398)"
id="g3795-4">
<path
inkscape:connector-curvature="0"
sodipodi:nodetypes="zsszssz"
id="path4152-9"
d="m 23.941292,8.0359216 c -7.625145,0 -13.825221,2.1315284 -13.825222,5.4762604 v 6.557844 c 0,3.344734 6.200076,5.647898 13.825222,5.647898 7.625146,0 14.072102,-2.303164 14.0721,-5.647898 v -6.557844 c 0,-3.344733 -6.446955,-5.4762604 -14.0721,-5.4762604 z"
style="color:#000000;display:block;overflow:visible;visibility:visible;fill:url(#linearGradient3939);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.769;marker:none" />
<ellipse
style="color:#000000;display:block;overflow:visible;visibility:visible;fill:#c3c3c3;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient3941);stroke-width:1.00493;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none"
id="path3364-4"
transform="matrix(1.037291,0,0,0.954619,-0.894979,-0.0757206)"
cx="24"
cy="14.071428"
rx="12.857142"
ry="5.5" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:block;overflow:visible;visibility:visible;opacity:0.493671;fill:url(#linearGradient3943);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none"
d="m 13.642858,17.999999 0.256127,5.876067 4.672444,1.083684 -0.142857,-5.602608 c 0,0 2.071428,-0.03981 5.428571,-0.254094 -5.215743,-0.233073 -11.183373,-2.725109 -13.214285,-4.179 1.416899,2.092176 3,3.075951 3,3.075951 z"
id="path6922-6"
sodipodi:nodetypes="ccccccc" />
</g>
<g
id="g3795">
<path
inkscape:connector-curvature="0"
sodipodi:nodetypes="zsszssz"
id="path4152"
d="m 23.941292,8.0373063 c -7.625145,0 -13.825221,2.1301437 -13.825222,5.4748757 v 6.557844 c 0,3.344734 6.200076,5.647898 13.825222,5.647898 7.625146,0 14.072102,-2.303164 14.0721,-5.647898 v -6.557844 c 0,-3.344733 -6.446955,-5.4748757 -14.0721,-5.4748757 z"
style="color:#000000;display:block;overflow:visible;visibility:visible;fill:url(#linearGradient3945);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.769;marker:none" />
<ellipse
style="color:#000000;display:block;overflow:visible;visibility:visible;fill:#c3c3c3;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient3947);stroke-width:1.00493;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none"
id="path3364"
transform="matrix(1.037291,0,0,0.954619,-0.894979,-0.0757206)"
cx="24"
cy="14.071428"
rx="12.857142"
ry="5.5" />
<path
inkscape:connector-curvature="0"
style="color:#000000;display:block;overflow:visible;visibility:visible;opacity:0.493671;fill:url(#linearGradient3949);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none"
d="m 13.642858,17.999999 0.256127,5.876067 4.672444,1.083684 -0.142857,-5.602608 c 0,0 2.071428,-0.04037 5.428571,-0.254655 -5.215743,-0.233073 -11.183373,-2.724109 -13.214285,-4.178 1.416899,2.092176 3,3.075512 3,3.075512 z"
id="path6922"
sodipodi:nodetypes="ccccccc" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

View File

@ -0,0 +1,18 @@
#include "AboutDialog.h"
#include "ui_AboutDialog.h"
#include "Application.h"
AboutDialog::AboutDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::AboutDialog)
{
ui->setupUi(this);
this->setFixedSize(this->width(), this->height());
this->setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
ui->label_version->setText(Application::versionInformation());
}
AboutDialog::~AboutDialog()
{
delete ui;
}

View File

@ -0,0 +1,22 @@
#ifndef ABOUTDIALOG_H
#define ABOUTDIALOG_H
#include "WBFZExchangePluginAPI.h"
#include <QDialog>
namespace Ui {
class AboutDialog;
}
class AboutDialog : public QDialog
{
Q_OBJECT
public:
explicit AboutDialog(QWidget *parent = nullptr);
~AboutDialog();
private:
Ui::AboutDialog *ui;
};
#endif

View File

@ -0,0 +1,164 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AboutDialog</class>
<widget class="QDialog" name="AboutDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>653</width>
<height>502</height>
</rect>
</property>
<property name="windowTitle">
<string>About DB Browser for SQLite</string>
</property>
<property name="windowIcon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/appicon</normaloff>:/icons/appicon</iconset>
</property>
<property name="sizeGripEnabled">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_version">
<property name="text">
<string>Version</string>
</property>
<property name="margin">
<number>5</number>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label_imagelogo">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>128</width>
<height>128</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="icons/icons.qrc">:/icons/appicon</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;DB Browser for SQLite is an open source, freeware visual tool used to create, design and edit SQLite database files.&lt;/p&gt;&lt;p&gt;It is bi-licensed under the Mozilla Public License Version 2, as well as the GNU General Public License Version 3 or later. You can modify or redistribute it under the conditions of these licenses.&lt;/p&gt;&lt;p&gt;See &lt;a href=&quot;http://www.gnu.org/licenses/gpl.html&quot;&gt;http://www.gnu.org/licenses/gpl.html&lt;/a&gt; and &lt;a href=&quot;https://www.mozilla.org/MPL/2.0/index.txt&quot;&gt;https://www.mozilla.org/MPL/2.0/index.txt&lt;/a&gt; for details.&lt;/p&gt;&lt;p&gt;For more information on this program please visit our website at: &lt;a href=&quot;http://sqlitebrowser.org&quot;&gt;http://sqlitebrowser.org&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-size:small;&quot;&gt;This software uses the GPL/LGPL Qt Toolkit from &lt;/span&gt;&lt;a href=&quot;http://qt-project.org/&quot;&gt;&lt;span style=&quot; font-size:small;&quot;&gt;http://qt-project.org/&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:small;&quot;&gt;&lt;br/&gt;See &lt;/span&gt;&lt;a href=&quot;http://qt-project.org/doc/qt-5/licensing.html&quot;&gt;&lt;span style=&quot; font-size:small;&quot;&gt;http://qt-project.org/doc/qt-5/licensing.html&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:small;&quot;&gt; for licensing terms and information.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-size:small;&quot;&gt;It also uses the Silk icon set by Mark James licensed under a Creative Commons Attribution 2.5 and 3.0 license.&lt;br/&gt;See &lt;/span&gt;&lt;a href=&quot;http://www.famfamfam.com/lab/icons/silk/&quot;&gt;&lt;span style=&quot; font-size:small;&quot;&gt;http://www.famfamfam.com/lab/icons/silk/&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:small;&quot;&gt; for details.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="icons/icons.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>AboutDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>340</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>AboutDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>340</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,367 @@
#include "AddRecordDialog.h"
#include "ui_AddRecordDialog.h"
#include "sqlitedb.h"
#include "Settings.h"
#include <QMessageBox>
#include <QPushButton>
#include <QKeyEvent>
#include <QStyledItemDelegate>
#include <QWhatsThis>
#include <QLineEdit>
#include <QMenu>
class NullLineEdit: public QLineEdit {
Q_OBJECT
private:
bool m_isNull;
public:
explicit NullLineEdit(QWidget* parent=nullptr): QLineEdit(parent), m_isNull (true) {}
bool isNull() {return m_isNull;}
void setNull(bool value) {
if (value) {
clear();
setStyleSheet("QLineEdit{ font-style: italic; }");
setPlaceholderText(Settings::getValue("databrowser", "null_text").toString());
setModified(false);
} else {
setStyleSheet("");
setPlaceholderText("");
}
m_isNull = value;
}
protected:
void contextMenuEvent(QContextMenuEvent *event)
{
QMenu* editContextMenu = createStandardContextMenu();
QAction* nullAction = new QAction(QIcon(":/icons/set_to_null"), tr("Set to NULL"), editContextMenu);
connect(nullAction, &QAction::triggered, [&]() {
setNull(true);
});
nullAction->setShortcut(QKeySequence(tr("Alt+Del")));
editContextMenu->addSeparator();
editContextMenu->addAction(nullAction);
editContextMenu->exec(event->globalPos());
delete editContextMenu;
}
void keyPressEvent(QKeyEvent *evt) {
// Alt+Del sets field to NULL
if((evt->modifiers() & Qt::AltModifier) && (evt->key() == Qt::Key_Delete))
setNull(true);
else {
// Remove any possible NULL mark when user starts typing
setStyleSheet("");
setPlaceholderText("");
QLineEdit::keyPressEvent(evt);
}
}
};
// Styled Item Delegate for non-editable columns (all except Value)
class NoEditDelegate: public QStyledItemDelegate {
public:
explicit NoEditDelegate(QObject* parent=nullptr): QStyledItemDelegate(parent) {}
QWidget* createEditor(QWidget* /* parent */, const QStyleOptionViewItem& /* option */, const QModelIndex& /* index */) const override {
return nullptr;
}
};
// Styled Item Delegate for editable columns (Value)
class EditDelegate: public QStyledItemDelegate {
public:
explicit EditDelegate(QObject* parent=nullptr): QStyledItemDelegate(parent) {}
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem& /* option */, const QModelIndex& /* index */) const override {
return new NullLineEdit(parent);
}
void setEditorData(QWidget *editor, const QModelIndex &index) const override {
NullLineEdit* lineEditor = static_cast<NullLineEdit*>(editor);
// Set the editor in the null state (unless the user has actually written NULL)
if (index.model()->data(index, Qt::UserRole).isNull() &&
index.model()->data(index, Qt::DisplayRole) == Settings::getValue("databrowser", "null_text"))
lineEditor->setNull(true);
else {
QStyledItemDelegate::setEditorData(editor, index);
lineEditor->setNull(false);
}
}
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override {
NullLineEdit* lineEditor = static_cast<NullLineEdit*>(editor);
// Restore NULL text (unless the user has already modified the value)
if (lineEditor->isNull() && !lineEditor->isModified()) {
model->setData(index, Settings::getValue("databrowser", "null_text"), Qt::DisplayRole);
model->setData(index, QVariant(), Qt::UserRole);
} else {
// Get isModified flag before calling setModelData
bool modified = lineEditor->isModified();
QStyledItemDelegate::setModelData(editor, model, index);
// Copy the just edited data to the user role, so it can be later used in the SQL insert statement.
if (modified) {
lineEditor->setNull(false);
model->setData(index, model->data(index, Qt::EditRole), Qt::UserRole);
}
}
}
};
AddRecordDialog::AddRecordDialog(DBBrowserDB& db, const sqlb::ObjectIdentifier& tableName, QWidget* parent, const std::vector<std::string>& _pseudo_pk)
: QDialog(parent),
ui(new Ui::AddRecordDialog),
pdb(db),
curTable(tableName),
pseudo_pk(_pseudo_pk)
{
// Create UI
ui->setupUi(this);
connect(ui->treeWidget, &QTreeWidget::itemChanged, this, &AddRecordDialog::itemChanged);
populateFields();
ui->sqlTextEdit->setReadOnly(true);
// Update UI
ui->treeWidget->resizeColumnToContents(kName);
ui->treeWidget->resizeColumnToContents(kType);
ui->treeWidget->setFrameShape(QFrame::Box);
}
AddRecordDialog::~AddRecordDialog()
{
delete ui;
}
void AddRecordDialog::keyPressEvent(QKeyEvent *evt)
{
if((evt->modifiers() & Qt::ControlModifier)
&& (evt->key() == Qt::Key_Enter || evt->key() == Qt::Key_Return))
{
accept();
return;
}
if(evt->key() == Qt::Key_Enter || evt->key() == Qt::Key_Return)
return;
QDialog::keyPressEvent(evt);
}
void AddRecordDialog::setDefaultsStyle(QTreeWidgetItem* item)
{
// Default values are displayed with the style configured for NULL values in the Data Browser.
QFont font;
font.setItalic(true);
item->setData(kValue, Qt::FontRole, font);
item->setData(kValue, Qt::BackgroundRole, QColor(Settings::getValue("databrowser", "null_bg_colour").toString()));
item->setData(kValue, Qt::ForegroundRole, QColor(Settings::getValue("databrowser", "null_fg_colour").toString()));
}
void AddRecordDialog::populateFields()
{
// Block the itemChanged signal or the SQL text will be updated while filling the treewidget.
ui->treeWidget->blockSignals(true);
ui->treeWidget->clear();
// Allow all Edit Triggers, but they will only apply to the columns with
// editors (Value)
ui->treeWidget->setEditTriggers(QAbstractItemView::AllEditTriggers);
// Disallow edition of columns except Value
ui->treeWidget->setItemDelegateForColumn(kName, new NoEditDelegate(this));
ui->treeWidget->setItemDelegateForColumn(kType, new NoEditDelegate(this));
ui->treeWidget->setItemDelegateForColumn(kValue, new EditDelegate(this));
sqlb::FieldVector fields;
std::vector<sqlb::ConstraintPtr> fks;
sqlb::StringVector pk;
bool auto_increment = false;
// Initialize fields, fks and pk differently depending on whether it's a table or a view.
const sqlb::ObjectPtr obj = pdb.getObjectByName(curTable);
if (obj->type() == sqlb::Object::Table)
{
sqlb::TablePtr m_table = pdb.getObjectByName<sqlb::Table>(curTable);
fields = m_table->fields;
for(const sqlb::Field& f : fields)
fks.push_back(m_table->constraint({f.name()}, sqlb::Constraint::ForeignKeyConstraintType));
const auto pk_constraint = m_table->primaryKey();
if(pk_constraint)
{
pk = pk_constraint->columnList();
auto_increment = pk_constraint->autoIncrement();
}
} else {
sqlb::ViewPtr m_view = pdb.getObjectByName<sqlb::View>(curTable);
fields = m_view->fields;
fks.resize(fields.size(), sqlb::ConstraintPtr(nullptr));
pk = pseudo_pk;
}
for(uint i = 0; i < fields.size(); i++)
{
const sqlb::Field& f = fields[i];
QTreeWidgetItem *tbitem = new QTreeWidgetItem(ui->treeWidget);
tbitem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable);
tbitem->setText(kName, QString::fromStdString(f.name()));
tbitem->setText(kType, QString::fromStdString(f.type()));
tbitem->setData(kType, Qt::UserRole, f.affinity());
// NOT NULL fields are indicated in bold.
if (f.notnull()) {
QFont font;
font.setBold(true);
tbitem->setData(kName, Qt::FontRole, font);
}
if (contains(pk, f.name()))
tbitem->setIcon(kName, QIcon(":/icons/field_key"));
else if (fks[i])
tbitem->setIcon(kName, QIcon(":/icons/field_fk"));
else
tbitem->setIcon(kName, QIcon(":/icons/field"));
QString toolTip;
if (auto_increment && contains(pk, f.name()))
toolTip.append(tr("Auto-increment\n"));
if (f.unique())
toolTip.append(tr("Unique constraint\n"));
if (!f.check().empty())
toolTip.append(tr("Check constraint:\t %1\n").arg(QString::fromStdString(f.check())));
auto fk = std::dynamic_pointer_cast<sqlb::ForeignKeyClause>(fks[i]);
if(fk)
toolTip.append(tr("Foreign key:\t %1\n").arg(QString::fromStdString(fk->toString())));
setDefaultsStyle(tbitem);
// Display Role is used for displaying the default values.
// Only when they are changed, the User Role is updated and then used in the INSERT query.
if (!f.defaultValue().empty()) {
QString defaultValue = QString::fromStdString(f.defaultValue());
tbitem->setData(kValue, Qt::DisplayRole, defaultValue);
toolTip.append(tr("Default value:\t %1\n").arg(defaultValue));
} else
tbitem->setData(kValue, Qt::DisplayRole, Settings::getValue("databrowser", "null_text"));
if (!toolTip.isEmpty()) {
// Chop last end-of-line
toolTip.chop(1);
tbitem->setToolTip(kValue, toolTip);
tbitem->setToolTip(kType, toolTip);
}
}
updateSqlText();
// Enable signals from tree widget
ui->treeWidget->blockSignals(false);
}
void AddRecordDialog::accept()
{
if(!pdb.executeSQL(ui->sqlTextEdit->text().toStdString()))
{
QMessageBox::warning(
this,
QApplication::applicationName(),
tr("Error adding record. Message from database engine:\n\n%1").arg(pdb.lastError()));
return;
}
QDialog::accept();
}
void AddRecordDialog::updateSqlText()
{
QString stmt = QString("INSERT INTO %1").arg(QString::fromStdString(curTable.toString()));
QStringList vals;
QStringList fields;
// If the User Role of the Value column is not null, the entered value is used
// in the INSERT statement. Otherwise, SQLite just uses the default value for the field.
for(int i = 0; i < ui->treeWidget->topLevelItemCount(); ++i)
{
QTreeWidgetItem *item = ui->treeWidget->topLevelItem(i);
// User role contains now values entered by the user, that we actually need to insert.
QVariant value = item->data(kValue, Qt::UserRole);
if (!value.isNull()) {
bool isNumeric;
fields << sqlb::escapeIdentifier(item->text(kName));
value.toDouble(&isNumeric);
// If it has a numeric format and has no text affinity, do not quote it.
if (isNumeric && item->data(kType, Qt::UserRole).toInt() != sqlb::Field::TextAffinity)
vals << value.toString();
else
vals << sqlb::escapeString(value.toString());
}
}
if(fields.empty())
{
stmt.append(" DEFAULT VALUES;");
} else {
stmt.append("\n(");
stmt.append(fields.join(", "));
stmt.append(")\nVALUES (");
stmt.append(vals.join(", "));
stmt.append(");");
}
ui->sqlTextEdit->setText(stmt);
}
void AddRecordDialog::itemChanged(QTreeWidgetItem *item, int column)
{
if (item->data(column, Qt::UserRole).isNull())
setDefaultsStyle(item);
else {
// Restore default fore/background for the value column,
// since the value has changed away from the default.
QFont font;
font.setItalic(false);
item->setData(column, Qt::FontRole, font);
item->setData(column, Qt::BackgroundRole, item->data(kName, Qt::BackgroundRole));
item->setData(column, Qt::ForegroundRole, item->data(kName, Qt::ForegroundRole));
}
updateSqlText();
}
void AddRecordDialog::help()
{
QWhatsThis::enterWhatsThisMode();
}
void AddRecordDialog::on_buttonBox_clicked(QAbstractButton* button)
{
if (button == ui->buttonBox->button(QDialogButtonBox::Cancel))
reject();
else if (button == ui->buttonBox->button(QDialogButtonBox::Save))
accept();
else if (button == ui->buttonBox->button(QDialogButtonBox::Help))
help();
else if (button == ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)) {
if (QMessageBox::warning(this,
QApplication::applicationName(),
tr("Are you sure you want to restore all the entered values to their defaults?"),
QMessageBox::RestoreDefaults | QMessageBox::Cancel,
QMessageBox::Cancel) == QMessageBox::RestoreDefaults)
populateFields();
}
}
#include "AddRecordDialog.moc"

View File

@ -0,0 +1,52 @@
#ifndef ADDRECORDDIALOG_H
#define ADDRECORDDIALOG_H
#include "WBFZExchangePluginAPI.h"
#include "sql/ObjectIdentifier.h"
#include <QDialog>
class DBBrowserDB;
class QTreeWidgetItem;
namespace Ui {
class AddRecordDialog;
}
class QAbstractButton;
class AddRecordDialog : public QDialog
{
Q_OBJECT
public:
explicit AddRecordDialog(DBBrowserDB& pdb, const sqlb::ObjectIdentifier& tableName, QWidget* parent = nullptr, const std::vector<std::string>& pseudo_pk = {});
~AddRecordDialog() override;
protected:
void keyPressEvent(QKeyEvent *evt) override;
private:
enum Columns {
kName = 0,
kType = 1,
kValue = 2,
};
void updateSqlText();
void populateFields();
void setDefaultsStyle(QTreeWidgetItem* item);
private slots:
void accept() override;
void itemChanged(QTreeWidgetItem* item, int column);
void help();
void on_buttonBox_clicked(QAbstractButton* button);
private:
Ui::AddRecordDialog* ui;
DBBrowserDB& pdb;
sqlb::ObjectIdentifier curTable;
std::vector<std::string> pseudo_pk;
};
#endif

View File

@ -0,0 +1,158 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AddRecordDialog</class>
<widget class="QDialog" name="AddRecordDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>650</width>
<height>500</height>
</rect>
</property>
<property name="windowTitle">
<string>Add New Record</string>
</property>
<property name="windowIcon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/table</normaloff>:/icons/table</iconset>
</property>
<property name="sizeGripEnabled">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QGroupBox" name="groupFields">
<property name="title">
<string>Enter values for the new record considering constraints. Fields in bold are mandatory.</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QTreeWidget" name="treeWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>140</height>
</size>
</property>
<property name="whatsThis">
<string>In the Value column you can specify the value for the field identified in the Name column. The Type column indicates the type of the field. Default values are displayed in the same style as NULL values.</string>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<column>
<property name="text">
<string>Name</string>
</property>
</column>
<column>
<property name="text">
<string>Type</string>
</property>
</column>
<column>
<property name="text">
<string>Value</string>
</property>
<property name="toolTip">
<string>Values to insert. Pre-filled default values are inserted automatically unless they are changed.</string>
</property>
</column>
</widget>
<widget class="SqlTextEdit" name="sqlTextEdit" native="true">
<property name="whatsThis">
<string>When you edit the values in the upper frame, the SQL query for inserting this new record is shown here. You can edit manually the query before saving.</string>
</property>
<property name="readOnly" stdset="0">
<bool>true</bool>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Save&lt;/span&gt; will submit the shown SQL statement to the database for inserting the new record.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Restore Defaults&lt;/span&gt; will restore the initial values in the &lt;span style=&quot; font-weight:600;&quot;&gt;Value&lt;/span&gt; column.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Cancel&lt;/span&gt; will close this dialog without executing the query.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::RestoreDefaults|QDialogButtonBox::Save</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>SqlTextEdit</class>
<extends>QWidget</extends>
<header>sqltextedit.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>treeWidget</tabstop>
<tabstop>sqlTextEdit</tabstop>
</tabstops>
<resources>
<include location="icons/icons.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>clicked(QAbstractButton*)</signal>
<receiver>AddRecordDialog</receiver>
<slot>on_buttonBox_clicked(QAbstractButton*)</slot>
<hints>
<hint type="sourcelabel">
<x>324</x>
<y>477</y>
</hint>
<hint type="destinationlabel">
<x>324</x>
<y>249</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>itemChanged()</slot>
</slots>
</ui>

View File

@ -0,0 +1,280 @@
#include <QFile>
#include <QFileOpenEvent>
#include <QTranslator>
#include <QTextCodec>
#include <QLibraryInfo>
#include <QLocale>
#include <QDebug>
#include <QAction>
#include <QFileInfo>
#include "Application.h"
#include "SqliteDBMainWindow.h"
//#include "RemoteNetwork.h"
#include "Settings.h"
#include "version.h"
#include "sqlitedb.h"
Application::Application(int& argc, char** argv) :
QApplication(argc, argv)
{
// Set organisation and application names
setOrganizationName(u8"lamp");
setApplicationName(u8"ÊôÐÔ±í¹ÜÀíÓë±à¼­");
// Set character encoding to UTF8
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
// Load translations
bool ok;
QString name = Settings::getValue("General", "language").toString();
// First of all try to load the application translation file.
m_translatorApp = new QTranslator(this);
ok = m_translatorApp->load("sqlb_" + name,
QCoreApplication::applicationDirPath() + "/translations");
// If failed then try to load .qm file from resources
if (ok == false) {
ok = m_translatorApp->load("sqlb_" + name, ":/translations");
}
if (ok == true) {
Settings::setValue("General", "language", name);
installTranslator(m_translatorApp);
// The application translation file has been found and loaded.
// And now try to load a Qt translation file.
// Search path:
// 1) standard Qt translations directory;
// 2) the application translations directory.
m_translatorQt = new QTranslator(this);
ok = m_translatorQt->load("qt_" + name,
QLibraryInfo::location(QLibraryInfo::TranslationsPath));
if (ok == false)
ok = m_translatorQt->load("qt_" + name, "translations");
if (ok == true)
installTranslator(m_translatorQt);
} else {
// Set the correct locale so that the program won't erroneously detect
// a language change when the user toggles settings for the first time.
// (it also prevents the program from always looking for a translation on launch)
Settings::setValue("General", "language", "en_US");
// Don't install a translator for Qt texts if no translator for DB4S texts could be loaded
m_translatorQt = nullptr;
}
// On Windows, add the plugins subdirectory to the list of library directories. We need this
// for Qt to search for more image format plugins.
#ifdef Q_WS_WIN
QApplication::addLibraryPath(QApplication::applicationDirPath() + "/plugins");
#endif
// Work around a bug in QNetworkAccessManager which sporadically causes high pings for Wifi connections
// See https://bugreports.qt.io/browse/QTBUG-40332
qputenv("QT_BEARER_POLL_TIMEOUT", QByteArray::number(INT_MAX));
// Remember default font size
Settings::rememberDefaultFontSize(font().pointSize());
// Parse command line
QString fileToOpen;
QString tableToBrowse;
QStringList sqlToExecute;
bool readOnly = false;
m_dontShowMainWindow = false;
for(int i=1;i<arguments().size();i++)
{
// Check next command line argument
if(arguments().at(i) == "-h" || arguments().at(i) == "--help")
{
// Help
qWarning() << qPrintable(tr("Usage: %1 [options] [<database>|<project>]\n").
arg(QFileInfo(argv[0]).fileName()));
qWarning() << qPrintable(tr("Possible command line arguments:"));
qWarning() << qPrintable(tr(" -h, --help Show command line options"));
qWarning() << qPrintable(tr(" -q, --quit Exit application after running scripts"));
qWarning() << qPrintable(tr(" -s, --sql <file> Execute this SQL file after opening the DB"));
qWarning() << qPrintable(tr(" -t, --table <table> Browse this table after opening the DB"));
qWarning() << qPrintable(tr(" -R, --read-only Open database in read-only mode"));
qWarning() << qPrintable(tr(" -o, --option <group>/<setting>=<value>"));
qWarning() << qPrintable(tr(" Run application with this setting temporarily set to value"));
qWarning() << qPrintable(tr(" -O, --save-option <group>/<setting>=<value>"));
qWarning() << qPrintable(tr(" Run application saving this value for this setting"));
qWarning() << qPrintable(tr(" -v, --version Display the current version"));
qWarning() << qPrintable(tr(" <database> Open this SQLite database"));
qWarning() << qPrintable(tr(" <project> Open this project file (*.sqbpro)"));
m_dontShowMainWindow = true;
} else if(arguments().at(i) == "-v" || arguments().at(i) == "--version") {
qWarning() << qPrintable(versionInformation());
m_dontShowMainWindow = true;
} else if(arguments().at(i) == "-s" || arguments().at(i) == "--sql") {
// Run SQL file: If file exists add it to list of scripts to execute
if(++i >= arguments().size())
qWarning() << qPrintable(tr("The -s/--sql option requires an argument"));
else if(!QFile::exists(arguments().at(i)))
qWarning() << qPrintable(tr("The file %1 does not exist").arg(arguments().at(i)));
else
sqlToExecute.append(arguments().at(i));
} else if(arguments().at(i) == "-t" || arguments().at(i) == "--table") {
if(++i >= arguments().size())
qWarning() << qPrintable(tr("The -t/--table option requires an argument"));
else
tableToBrowse = arguments().at(i);
} else if(arguments().at(i) == "-q" || arguments().at(i) == "--quit") {
m_dontShowMainWindow = true;
} else if(arguments().at(i) == "-R" || arguments().at(i) == "--read-only") {
readOnly = true;
} else if(arguments().at(i) == "-o" || arguments().at(i) == "--option" ||
arguments().at(i) == "-O" || arguments().at(i) == "--save-option") {
const QString optionWarning = tr("The -o/--option and -O/--save-option options require an argument in the form group/setting=value");
bool saveToDisk = arguments().at(i) == "-O" || arguments().at(i) == "--save-option";
if(++i >= arguments().size())
qWarning() << qPrintable(optionWarning);
else {
QStringList option = arguments().at(i).split("=");
if (option.size() != 2)
qWarning() << qPrintable(optionWarning);
else {
QStringList setting = option.at(0).split("/");
if (setting.size() != 2)
qWarning() << qPrintable(optionWarning);
else {
QVariant value;
// Split string lists. This assumes they are always named "*list"
if (setting.at(1).endsWith("list", Qt::CaseInsensitive))
value = option.at(1).split(",");
else
value = option.at(1);
Settings::setValue(setting.at(0).toStdString(), setting.at(1).toStdString(), value, !saveToDisk);
}
}
}
} else {
// Other: Check if it's a valid file name
if(QFile::exists(arguments().at(i)))
fileToOpen = arguments().at(i);
else
qWarning() << qPrintable(tr("Invalid option/non-existant file: %1").arg(arguments().at(i)));
}
}
if(m_dontShowMainWindow) {
m_mainWindow = nullptr;
return;
}
// Show main window
m_mainWindow = new SqliteDBMainWindow();
m_mainWindow->show();
connect(this, &Application::lastWindowClosed, this, &Application::quit);
// Open database if one was specified
if(fileToOpen.size())
{
if(m_mainWindow->fileOpen(fileToOpen, false, readOnly))
{
// If database could be opened run the SQL scripts
for(const QString& f : sqlToExecute)
{
QFile file(f);
if(file.open(QIODevice::ReadOnly))
{
m_mainWindow->getDb().executeMultiSQL(file.readAll(), false, true);
file.close();
}
}
if(!sqlToExecute.isEmpty())
m_mainWindow->refresh();
// Jump to table if the -t/--table parameter was set
if(!tableToBrowse.isEmpty()) {
m_mainWindow->switchToBrowseDataTab(sqlb::ObjectIdentifier("main", tableToBrowse.toStdString()));
m_mainWindow->refresh();
}
}
}
}
Application::~Application()
{
if(m_mainWindow)
delete m_mainWindow;
}
bool Application::event(QEvent* event)
{
switch(event->type())
{
case QEvent::FileOpen:
m_mainWindow->fileOpen(static_cast<QFileOpenEvent*>(event)->file());
return true;
default:
return QApplication::event(event);
}
}
QString Application::versionString()
{
// Nightly builds use a patch number of 99, and for these we include the build date in the version string. For
// our release versions (which use any other patch number) we don't include the build date. This should avoid
// confusion about what is more important, version number or build date, and about different build dates for
// the same version. This also should help making release builds reproducible out of the box.
#if PATCH_VERSION == 99
return QString("%1 (%2)").arg(APP_VERSION, __DATE__);
#else
return QString("%1").arg(APP_VERSION);
#endif
}
QString Application::versionInformation()
{
QString sqlite_version, sqlcipher_version;
DBBrowserDB::getSqliteVersion(sqlite_version, sqlcipher_version);
if(sqlcipher_version.isNull())
sqlite_version = tr("SQLite Version ") + sqlite_version;
else
sqlite_version = tr("SQLCipher Version %1 (based on SQLite %2)").arg(sqlcipher_version, sqlite_version);
return
tr("DB Browser for SQLite Version %1.").arg(versionString() + "\n\n" +
tr("Built for %1, running on %2").arg(QSysInfo::buildAbi(), QSysInfo::currentCpuArchitecture()) + "\n" +
tr("Qt Version %1").arg(QT_VERSION_STR) + "\n" +
sqlite_version
);
}
void Application::reloadSettings()
{
// Network settings
//RemoteNetwork::get().reloadSettings();
// Font settings
QFont f = font();
f.setPointSize(Settings::getValue("General", "fontsize").toInt());
setFont(f);
}
// Functions for documenting the shortcuts in the user interface using native names
static QString shortcutsTip(const QList<QKeySequence>& keys)
{
QString tip;
if (!keys.isEmpty()) {
tip = " [";
for (const auto& shortcut : keys)
tip.append(shortcut.toString(QKeySequence::NativeText) + ", ");
tip.chop(2);
tip.append("]");
}
return tip;
}
void addShortcutsTooltip(QAction* action, const QList<QKeySequence>& extraKeys)
{
if (!action->shortcuts().isEmpty() || !extraKeys.isEmpty())
action->setToolTip(action->toolTip() + shortcutsTip(action->shortcuts() + extraKeys));
}

View File

@ -0,0 +1,43 @@
#ifndef APPLICATION_H
#define APPLICATION_H
#include "WBFZExchangePluginAPI.h"
#include <QApplication>
#include <QKeySequence>
class QAction;
class QTranslator;
class SqliteDBMainWindow;
class Application : public QApplication
{
Q_OBJECT
public:
explicit Application(int& argc, char** argv);
~Application() override;
bool dontShowMainWindow() const { return m_dontShowMainWindow; }
SqliteDBMainWindow* mainWindow() { return m_mainWindow; }
// DB4S version number as string
static QString versionString();
// Version of DB4S and dependencies as string
static QString versionInformation();
static void reloadSettings();
protected:
bool event(QEvent* event) override;
private:
bool m_dontShowMainWindow;
SqliteDBMainWindow* m_mainWindow;
QTranslator* m_translatorQt;
QTranslator* m_translatorApp;
};
void addShortcutsTooltip(QAction* action, const QList<QKeySequence>& extraKeys = QList<QKeySequence>());
#endif

View File

@ -0,0 +1,146 @@
#include "CipherDialog.h"
#include "ui_CipherDialog.h"
#include "sqlitedb.h"
#include <QPushButton>
#include <QRegularExpressionValidator>
#include <QtCore/qmath.h>
CipherDialog::CipherDialog(QWidget* parent, bool encrypt) :
QDialog(parent),
ui(new Ui::CipherDialog),
encryptMode(encrypt),
rawKeyValidator(new QRegularExpressionValidator(QRegularExpression("\\A(0x[a-fA-F0-9]+)\\Z")))
{
ui->setupUi(this);
int minimumPageSizeExponent = 9;
int maximumPageSizeExponent = 16;
int defaultPageSizeExponent = 10;
for(int exponent = minimumPageSizeExponent; exponent <= maximumPageSizeExponent; exponent++)
{
int pageSize = static_cast<int>(qPow(2, exponent));
ui->comboPageSize->addItem(QLocale().toString(pageSize), pageSize);
if (exponent == defaultPageSizeExponent)
ui->comboPageSize->setCurrentIndex(exponent - minimumPageSizeExponent);
}
ui->comboPageSize->setMinimumWidth(ui->editPassword->width());
if(encrypt)
{
ui->labelDialogDescription->setText(tr("Please set a key to encrypt the database.\nNote that if you change any of the other, optional, settings you'll need "
"to re-enter them as well every time you open the database file.\nLeave the password fields empty to disable the "
"encryption.\nThe encryption process might take some time and you should have a backup copy of your database! Unsaved "
"changes are applied before modifying the encryption."));
} else {
ui->labelDialogDescription->setText(tr("Please enter the key used to encrypt the database.\nIf any of the other settings were altered for this database file "
"you need to provide this information as well."));
ui->editPassword2->setVisible(false);
ui->labelPassword2->setVisible(false);
}
// Set the default encryption settings depending on the SQLCipher version we use
QString sqlite_version, sqlcipher_version;
DBBrowserDB::getSqliteVersion(sqlite_version, sqlcipher_version);
if(sqlcipher_version.startsWith('4'))
ui->radioEncryptionSqlCipher4->setChecked(true);
else
ui->radioEncryptionSqlCipher3->setChecked(true);
}
CipherDialog::~CipherDialog()
{
delete rawKeyValidator;
delete ui;
}
CipherSettings CipherDialog::getCipherSettings() const
{
CipherSettings::KeyFormats keyFormat = CipherSettings::getKeyFormat(ui->comboKeyFormat->currentIndex());
std::string password = ui->editPassword->text().toStdString();
int pageSize = ui->comboPageSize->itemData(ui->comboPageSize->currentIndex()).toInt();
CipherSettings cipherSettings;
cipherSettings.setKeyFormat(keyFormat);
cipherSettings.setPassword(password);
cipherSettings.setPageSize(pageSize);
cipherSettings.setKdfIterations(ui->spinKdfIterations->value());
cipherSettings.setHmacAlgorithm("HMAC_" + ui->comboHmacAlgorithm->currentText().toStdString());
cipherSettings.setKdfAlgorithm("PBKDF2_HMAC_" + ui->comboKdfAlgorithm->currentText().toStdString());
cipherSettings.setPlaintextHeaderSize(ui->plaintextHeaderSize->value());
return cipherSettings;
}
void CipherDialog::checkInputFields()
{
if(sender() == ui->comboKeyFormat)
{
CipherSettings::KeyFormats keyFormat = CipherSettings::getKeyFormat(ui->comboKeyFormat->currentIndex());
if(keyFormat == CipherSettings::KeyFormats::Passphrase)
{
ui->editPassword->setValidator(nullptr);
ui->editPassword2->setValidator(nullptr);
ui->editPassword->setPlaceholderText("");
} else if(keyFormat == CipherSettings::KeyFormats::RawKey) {
ui->editPassword->setValidator(rawKeyValidator);
ui->editPassword2->setValidator(rawKeyValidator);
ui->editPassword->setPlaceholderText("0x...");
}
ui->editPassword->setText("");
ui->editPassword2->setText("");
}
bool valid = true;
if(encryptMode)
valid = ui->editPassword->text() == ui->editPassword2->text();
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(valid);
}
void CipherDialog::toggleEncryptionSettings()
{
if(ui->radioEncryptionSqlCipher3->isChecked())
{
// SQLCipher3
ui->comboPageSize->setCurrentText(QLocale().toString(1024));
ui->spinKdfIterations->setValue(64000);
ui->comboHmacAlgorithm->setCurrentText("SHA1");
ui->comboKdfAlgorithm->setCurrentText("SHA1");
ui->plaintextHeaderSize->setValue(0);
ui->comboPageSize->setEnabled(false);
ui->spinKdfIterations->setEnabled(false);
ui->comboHmacAlgorithm->setEnabled(false);
ui->comboKdfAlgorithm->setEnabled(false);
ui->plaintextHeaderSize->setEnabled(false);
} else if(ui->radioEncryptionSqlCipher4->isChecked()) {
// SQLCipher4
ui->comboPageSize->setCurrentText(QLocale().toString(4096));
ui->spinKdfIterations->setValue(256000);
ui->comboHmacAlgorithm->setCurrentText("SHA512");
ui->comboKdfAlgorithm->setCurrentText("SHA512");
ui->plaintextHeaderSize->setValue(0);
ui->comboPageSize->setEnabled(false);
ui->spinKdfIterations->setEnabled(false);
ui->comboHmacAlgorithm->setEnabled(false);
ui->comboKdfAlgorithm->setEnabled(false);
ui->plaintextHeaderSize->setEnabled(false);
} else if(ui->radioEncryptionCustom->isChecked()) {
// Custom
ui->comboPageSize->setEnabled(true);
ui->spinKdfIterations->setEnabled(true);
ui->comboHmacAlgorithm->setEnabled(true);
ui->comboKdfAlgorithm->setEnabled(true);
ui->plaintextHeaderSize->setEnabled(true);
}
}

View File

@ -0,0 +1,36 @@
#ifndef CIPHERDIALOG_H
#define CIPHERDIALOG_H
#include "WBFZExchangePluginAPI.h"
#include <QDialog>
#include "CipherSettings.h"
class QRegularExpressionValidator;
namespace Ui {
class CipherDialog;
}
class CipherDialog : public QDialog
{
Q_OBJECT
public:
// Set the encrypt parameter to true when the dialog is used to encrypt a database;
// set it to false if the dialog is used to ask the user for the key to decrypt a file.
explicit CipherDialog(QWidget* parent, bool encrypt);
~CipherDialog() override;
CipherSettings getCipherSettings() const;
private:
Ui::CipherDialog* ui;
bool encryptMode;
QRegularExpressionValidator* rawKeyValidator;
private slots:
void checkInputFields();
void toggleEncryptionSettings();
};
#endif

View File

@ -0,0 +1,403 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CipherDialog</class>
<widget class="QDialog" name="CipherDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>712</width>
<height>299</height>
</rect>
</property>
<property name="windowTitle">
<string>SQLCipher encryption</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="labelDialogDescription"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="labelPassword">
<property name="text">
<string>&amp;Password</string>
</property>
<property name="buddy">
<cstring>editPassword</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="editPassword">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelPassword2">
<property name="text">
<string>&amp;Reenter password</string>
</property>
<property name="buddy">
<cstring>editPassword2</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="editPassword2">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QComboBox" name="comboKeyFormat">
<item>
<property name="text">
<string>Passphrase</string>
</property>
</item>
<item>
<property name="text">
<string>Raw key</string>
</property>
</item>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Encr&amp;yption settings</string>
</property>
<property name="buddy">
<cstring>radioEncryptionSqlCipher3</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QRadioButton" name="radioEncryptionSqlCipher3">
<property name="text">
<string>SQLCipher &amp;3 defaults</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioEncryptionSqlCipher4">
<property name="text">
<string>SQLCipher &amp;4 defaults</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioEncryptionCustom">
<property name="text">
<string>Custo&amp;m</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Page si&amp;ze</string>
</property>
<property name="buddy">
<cstring>comboPageSize</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="comboPageSize"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>&amp;KDF iterations</string>
</property>
<property name="buddy">
<cstring>spinKdfIterations</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="spinKdfIterations">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>1000000</number>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>HMAC algorithm</string>
</property>
<property name="buddy">
<cstring>comboHmacAlgorithm</cstring>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="comboHmacAlgorithm">
<item>
<property name="text">
<string notr="true">SHA512</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">SHA256</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">SHA1</string>
</property>
</item>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>KDF algorithm</string>
</property>
<property name="buddy">
<cstring>comboKdfAlgorithm</cstring>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="comboKdfAlgorithm">
<item>
<property name="text">
<string notr="true">SHA512</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">SHA256</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">SHA1</string>
</property>
</item>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Plaintext Header Size</string>
</property>
<property name="buddy">
<cstring>plaintextHeaderSize</cstring>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QSpinBox" name="plaintextHeaderSize">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>1000000</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>editPassword</tabstop>
<tabstop>editPassword2</tabstop>
<tabstop>comboKeyFormat</tabstop>
<tabstop>radioEncryptionSqlCipher3</tabstop>
<tabstop>radioEncryptionSqlCipher4</tabstop>
<tabstop>radioEncryptionCustom</tabstop>
<tabstop>comboPageSize</tabstop>
<tabstop>spinKdfIterations</tabstop>
<tabstop>comboHmacAlgorithm</tabstop>
<tabstop>comboKdfAlgorithm</tabstop>
<tabstop>plaintextHeaderSize</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>CipherDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>175</x>
<y>265</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>126</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>CipherDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>175</x>
<y>265</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>126</y>
</hint>
</hints>
</connection>
<connection>
<sender>editPassword</sender>
<signal>textChanged(QString)</signal>
<receiver>CipherDialog</receiver>
<slot>checkInputFields()</slot>
<hints>
<hint type="sourcelabel">
<x>150</x>
<y>38</y>
</hint>
<hint type="destinationlabel">
<x>155</x>
<y>26</y>
</hint>
</hints>
</connection>
<connection>
<sender>editPassword2</sender>
<signal>textChanged(QString)</signal>
<receiver>CipherDialog</receiver>
<slot>checkInputFields()</slot>
<hints>
<hint type="sourcelabel">
<x>446</x>
<y>79</y>
</hint>
<hint type="destinationlabel">
<x>206</x>
<y>46</y>
</hint>
</hints>
</connection>
<connection>
<sender>comboKeyFormat</sender>
<signal>currentIndexChanged(int)</signal>
<receiver>CipherDialog</receiver>
<slot>checkInputFields()</slot>
<hints>
<hint type="sourcelabel">
<x>670</x>
<y>38</y>
</hint>
<hint type="destinationlabel">
<x>665</x>
<y>0</y>
</hint>
</hints>
</connection>
<connection>
<sender>radioEncryptionSqlCipher3</sender>
<signal>toggled(bool)</signal>
<receiver>CipherDialog</receiver>
<slot>toggleEncryptionSettings()</slot>
<hints>
<hint type="sourcelabel">
<x>217</x>
<y>114</y>
</hint>
<hint type="destinationlabel">
<x>231</x>
<y>94</y>
</hint>
</hints>
</connection>
<connection>
<sender>radioEncryptionSqlCipher4</sender>
<signal>toggled(bool)</signal>
<receiver>CipherDialog</receiver>
<slot>toggleEncryptionSettings()</slot>
<hints>
<hint type="sourcelabel">
<x>353</x>
<y>117</y>
</hint>
<hint type="destinationlabel">
<x>407</x>
<y>97</y>
</hint>
</hints>
</connection>
<connection>
<sender>radioEncryptionCustom</sender>
<signal>toggled(bool)</signal>
<receiver>CipherDialog</receiver>
<slot>toggleEncryptionSettings()</slot>
<hints>
<hint type="sourcelabel">
<x>552</x>
<y>120</y>
</hint>
<hint type="destinationlabel">
<x>590</x>
<y>99</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>checkInputFields()</slot>
<slot>toggleEncryptionSettings()</slot>
</slots>
</ui>

View File

@ -0,0 +1,26 @@
#include "CipherSettings.h"
#include "sqlitedb.h"
CipherSettings::CipherSettings()
: keyFormat(Passphrase),
pageSize(0),
kdfIterations(0),
plaintextHeaderSize(0)
{
}
std::string CipherSettings::getPassword() const
{
if(keyFormat == Passphrase)
{
return sqlb::escapeString(password);
} else {
// Remove the '0x' part at the beginning
return "\"x'" + password.substr(2) + "'\"";
}
}
CipherSettings::KeyFormats CipherSettings::getKeyFormat(int rawKeyFormat)
{
return static_cast<CipherSettings::KeyFormats>(rawKeyFormat);
}

View File

@ -0,0 +1,50 @@
#ifndef CIPHERSETTINGS_H
#define CIPHERSETTINGS_H
#include <string>
class CipherSettings
{
public:
CipherSettings();
enum KeyFormats
{
Passphrase,
RawKey
};
KeyFormats getKeyFormat() const { return keyFormat; }
void setKeyFormat(const KeyFormats &value) { keyFormat = value; }
std::string getPassword() const;
void setPassword(const std::string& value) { password = value; }
int getPageSize() const { return pageSize; }
void setPageSize(int value) { pageSize = value; }
int getKdfIterations() const { return kdfIterations; }
void setKdfIterations(int value) { kdfIterations = value; }
int getPlaintextHeaderSize() const { return plaintextHeaderSize; }
void setPlaintextHeaderSize(int value) { plaintextHeaderSize = value; }
std::string getHmacAlgorithm() const { return hmacAlgorithm; }
void setHmacAlgorithm(const std::string& value) { hmacAlgorithm = value; }
std::string getKdfAlgorithm() const { return kdfAlgorithm; }
void setKdfAlgorithm(const std::string& value) { kdfAlgorithm = value; }
static KeyFormats getKeyFormat(int rawKeyFormat);
private:
KeyFormats keyFormat;
std::string password;
int pageSize;
int kdfIterations;
int plaintextHeaderSize;
std::string hmacAlgorithm;
std::string kdfAlgorithm;
};
#endif // CIPHERSETTINGS_H

View File

@ -0,0 +1,139 @@
#include <QMessageBox>
#include "ColumnDisplayFormatDialog.h"
#include "ui_ColumnDisplayFormatDialog.h"
#include "sql/sqlitetypes.h"
#include "sqlitedb.h"
ColumnDisplayFormatDialog::ColumnDisplayFormatDialog(DBBrowserDB& db, const sqlb::ObjectIdentifier& tableName, const QString& colname, const QString& current_format, QWidget* parent)
: QDialog(parent),
ui(new Ui::ColumnDisplayFormatDialog),
column_name(colname),
pdb(db),
curTable(tableName)
{
// Create UI
ui->setupUi(this);
ui->comboDisplayFormat->addItem(tr("Default"), "default");
ui->comboDisplayFormat->insertSeparator(ui->comboDisplayFormat->count());
ui->comboDisplayFormat->addItem(tr("Decimal number"), "decimal");
ui->comboDisplayFormat->addItem(tr("Exponent notation"), "exponent");
ui->comboDisplayFormat->addItem(tr("Hex blob"), "hexblob");
ui->comboDisplayFormat->addItem(tr("Hex number"), "hex");
ui->comboDisplayFormat->addItem(tr("Octal number"), "octal");
ui->comboDisplayFormat->addItem(tr("Round number"), "round");
ui->comboDisplayFormat->insertSeparator(ui->comboDisplayFormat->count());
ui->comboDisplayFormat->addItem(tr("Apple NSDate to date"), "appleDate");
ui->comboDisplayFormat->addItem(tr("Java epoch (milliseconds) to date"), "javaEpoch");
ui->comboDisplayFormat->addItem(tr(".NET DateTime.Ticks to date"), "dotNetTicks");
ui->comboDisplayFormat->addItem(tr("Julian day to date"), "julian");
ui->comboDisplayFormat->addItem(tr("Unix epoch to date"), "epoch");
ui->comboDisplayFormat->addItem(tr("Unix epoch to local time"), "epochLocalTime");
ui->comboDisplayFormat->addItem(tr("Windows DATE to date"), "winDate");
ui->comboDisplayFormat->addItem(tr("Date as dd/mm/yyyy"), "ddmmyyyyDate");
ui->comboDisplayFormat->insertSeparator(ui->comboDisplayFormat->count());
ui->comboDisplayFormat->addItem(tr("Lower case"), "lower");
ui->comboDisplayFormat->addItem(tr("Upper case"), "upper");
ui->comboDisplayFormat->insertSeparator(ui->comboDisplayFormat->count());
ui->comboDisplayFormat->addItem(tr("Custom"), "custom");
ui->labelDisplayFormat->setText(ui->labelDisplayFormat->text().arg(column_name));
formatFunctions["decimal"] = "printf('%d', " + sqlb::escapeIdentifier(column_name) + ")";
formatFunctions["exponent"] = "printf('%e', " + sqlb::escapeIdentifier(column_name) + ")";
formatFunctions["hexblob"] = "hex(" + sqlb::escapeIdentifier(column_name) + ")";
formatFunctions["hex"] = "printf('0x%x', " + sqlb::escapeIdentifier(column_name) + ")";
formatFunctions["octal"] = "printf('%o', " + sqlb::escapeIdentifier(column_name) + ")";
formatFunctions["round"] = "round(" + sqlb::escapeIdentifier(column_name) + ")";
formatFunctions["appleDate"] = "datetime('2001-01-01', " + sqlb::escapeIdentifier(column_name) + " || ' seconds')";
formatFunctions["javaEpoch"] = "strftime('%Y-%m-%d %H:%M:%S.', " + sqlb::escapeIdentifier(column_name) +
"/1000, 'unixepoch') || (" + sqlb::escapeIdentifier(column_name) + "%1000)";
formatFunctions["dotNetTicks"] = "datetime(" + sqlb::escapeIdentifier(column_name) + " / 10000000 - 62135596800, 'unixepoch')";
formatFunctions["julian"] = "datetime(" + sqlb::escapeIdentifier(column_name) + ")";
formatFunctions["epoch"] = "datetime(" + sqlb::escapeIdentifier(column_name) + ", 'unixepoch')";
formatFunctions["epochLocalTime"] = "datetime(" + sqlb::escapeIdentifier(column_name) + ", 'unixepoch', 'localtime')";
formatFunctions["winDate"] = "datetime('1899-12-30', " + sqlb::escapeIdentifier(column_name) + " || ' days')";
formatFunctions["ddmmyyyyDate"] = "strftime('%d/%m/%Y', " + sqlb::escapeIdentifier(column_name) + ")";
formatFunctions["lower"] = "lower(" + sqlb::escapeIdentifier(column_name) + ")";
formatFunctions["upper"] = "upper(" + sqlb::escapeIdentifier(column_name) + ")";
// Set the current format, if it's empty set the default format
if(current_format.isEmpty())
{
ui->comboDisplayFormat->setCurrentIndex(0);
updateSqlCode();
} else {
// When it doesn't match any predefined format, it is considered custom
QString formatName = "custom";
auto it = std::find_if(formatFunctions.begin(), formatFunctions.end(), [current_format](const std::pair<std::string, QString>& s) {
return s.second == current_format;
});
if(it != formatFunctions.end())
formatName = QString::fromStdString(it->first);
ui->comboDisplayFormat->setCurrentIndex(ui->comboDisplayFormat->findData(formatName));
ui->editDisplayFormat->setText(current_format);
}
}
ColumnDisplayFormatDialog::~ColumnDisplayFormatDialog()
{
delete ui;
}
QString ColumnDisplayFormatDialog::selectedDisplayFormat() const
{
if(ui->comboDisplayFormat->currentData().toString() == "default")
return QString();
else
return ui->editDisplayFormat->text();
}
void ColumnDisplayFormatDialog::updateSqlCode()
{
std::string format = ui->comboDisplayFormat->currentData().toString().toStdString();
if(format == "default")
ui->editDisplayFormat->setText(sqlb::escapeIdentifier(column_name));
else if(format != "custom")
ui->editDisplayFormat->setText(formatFunctions.at(format));
}
void ColumnDisplayFormatDialog::accept()
{
QString errorMessage;
// Accept the SQL code if it's the column name (default), it contains a function invocation applied to the column name and it can be
// executed without errors returning only one column.
// Users could still devise a way to break this, but this is considered good enough for letting them know about simple incorrect
// cases.
if(!(ui->editDisplayFormat->text() == sqlb::escapeIdentifier(column_name) ||
ui->editDisplayFormat->text().contains(QRegularExpression("[a-z]+[a-z_0-9]* *\\(.*" + QRegularExpression::escape(sqlb::escapeIdentifier(column_name)) + ".*\\)", QRegularExpression::CaseInsensitiveOption))))
errorMessage = tr("Custom display format must contain a function call applied to %1").arg(sqlb::escapeIdentifier(column_name));
else {
// Execute a query using the display format and check that it only returns one column.
int customNumberColumns = 0;
DBBrowserDB::execCallback callback = [&customNumberColumns](int numberColumns, std::vector<QByteArray>, std::vector<QByteArray>) -> bool {
customNumberColumns = numberColumns;
// Return false so the query is not aborted and no error is reported.
return false;
};
if(!pdb.executeSQL("SELECT " + ui->editDisplayFormat->text().toStdString() + " FROM " + curTable.toString() + " LIMIT 1",
false, true, callback))
errorMessage = tr("Error in custom display format. Message from database engine:\n\n%1").arg(pdb.lastError());
else if(customNumberColumns != 1)
errorMessage = tr("Custom display format must return only one column but it returned %1.").arg(customNumberColumns);
}
if(!errorMessage.isEmpty())
QMessageBox::warning(this, QApplication::applicationName(), errorMessage);
else
QDialog::accept();
}
void ColumnDisplayFormatDialog::setCustom(bool modified)
{
// If the SQL code is modified by user, select the custom value in the combo-box
if(modified && ui->editDisplayFormat->hasFocus())
ui->comboDisplayFormat->setCurrentIndex(ui->comboDisplayFormat->findData("custom"));
}

View File

@ -0,0 +1,41 @@
#ifndef COLUMNDISPLAYFORMATDIALOG_H
#define COLUMNDISPLAYFORMATDIALOG_H
#include "WBFZExchangePluginAPI.h"
#include <QDialog>
#include <unordered_map>
#include "sql/ObjectIdentifier.h"
class QString;
class DBBrowserDB;
namespace Ui {
class ColumnDisplayFormatDialog;
}
class ColumnDisplayFormatDialog : public QDialog
{
Q_OBJECT
public:
explicit ColumnDisplayFormatDialog(DBBrowserDB& db, const sqlb::ObjectIdentifier& tableName, const QString& colname, const QString& current_format, QWidget* parent = nullptr);
~ColumnDisplayFormatDialog() override;
QString selectedDisplayFormat() const;
private slots:
void updateSqlCode();
void accept() override;
void setCustom(bool modified);
private:
Ui::ColumnDisplayFormatDialog* ui;
QString column_name;
std::unordered_map<std::string, QString> formatFunctions;
DBBrowserDB& pdb;
sqlb::ObjectIdentifier curTable;
};
#endif

View File

@ -0,0 +1,134 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ColumnDisplayFormatDialog</class>
<widget class="QDialog" name="ColumnDisplayFormatDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>624</width>
<height>205</height>
</rect>
</property>
<property name="windowTitle">
<string>Choose display format</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Display format</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="labelDisplayFormat">
<property name="text">
<string>Choose a display format for the column '%1' which is applied to each value prior to showing it.</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboDisplayFormat"/>
</item>
<item>
<widget class="SqlTextEdit" name="editDisplayFormat"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>SqlTextEdit</class>
<extends>QTextEdit</extends>
<header>sqltextedit.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>comboDisplayFormat</tabstop>
<tabstop>editDisplayFormat</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>ColumnDisplayFormatDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>227</x>
<y>188</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>204</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>ColumnDisplayFormatDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>295</x>
<y>194</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>204</y>
</hint>
</hints>
</connection>
<connection>
<sender>comboDisplayFormat</sender>
<signal>currentIndexChanged(int)</signal>
<receiver>ColumnDisplayFormatDialog</receiver>
<slot>updateSqlCode()</slot>
<hints>
<hint type="sourcelabel">
<x>125</x>
<y>69</y>
</hint>
<hint type="destinationlabel">
<x>244</x>
<y>4</y>
</hint>
</hints>
</connection>
<connection>
<sender>editDisplayFormat</sender>
<signal>modificationChanged(bool)</signal>
<receiver>ColumnDisplayFormatDialog</receiver>
<slot>setCustom(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>125</x>
<y>69</y>
</hint>
<hint type="destinationlabel">
<x>244</x>
<y>4</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>updateSqlCode()</slot>
<slot>setCustom()</slot>
</slots>
</ui>

View File

@ -0,0 +1,176 @@
#include "CondFormat.h"
#include "Settings.h"
#include "Data.h"
#include "sqlitedb.h"
#include <QAbstractTableModel>
CondFormat::Alignment CondFormat::fromCombinedAlignment(Qt::Alignment align)
{
if (align.testFlag(Qt::AlignLeft))
return AlignLeft;
if (align.testFlag(Qt::AlignRight))
return AlignRight;
if (align.testFlag(Qt::AlignCenter))
return AlignCenter;
if (align.testFlag(Qt::AlignJustify))
return AlignJustify;
return AlignLeft;
}
CondFormat::CondFormat(const QString& filter,
const QColor& foreground,
const QColor& background,
const QFont& font,
const Alignment alignment,
const QString& encoding)
: m_filter(filter),
m_bgColor(background),
m_fgColor(foreground),
m_font(font),
m_align(alignment)
{
if (!filter.isEmpty())
m_sqlCondition = filterToSqlCondition(filter, encoding);
}
CondFormat::CondFormat(const QString& filter,
const QAbstractTableModel* model,
const QModelIndex index,
const QString& encoding)
: m_filter(filter)
{
if (!filter.isEmpty())
m_sqlCondition = filterToSqlCondition(filter, encoding);
m_bgColor = QColor(model->data(index, Qt::BackgroundRole).toString());
m_fgColor = QColor(model->data(index, Qt::ForegroundRole).toString());
m_align = fromCombinedAlignment(Qt::Alignment(model->data(index, Qt::TextAlignmentRole).toInt()));
m_font.fromString(model->data(index, Qt::FontRole).toString());
}
std::string CondFormat::filterToSqlCondition(const QString& value, const QString& encoding)
{
if(value.isEmpty())
return {};
// Check for any special comparison operators at the beginning of the value string. If there are none default to LIKE.
QString op = "LIKE";
QString val, val2;
QString escape;
bool numeric = false, ok = false;
// range/BETWEEN operator
if (value.contains("~")) {
int sepIdx = value.indexOf('~');
val = value.mid(0, sepIdx);
val2 = value.mid(sepIdx+1);
float valf = val.toFloat(&ok);
if (ok) {
float val2f = val2.toFloat(&ok);
ok = ok && (valf < val2f);
}
}
if (ok) {
op = "BETWEEN";
numeric = true;
} else {
val.clear();
val2.clear();
if(value.left(2) == ">=" || value.left(2) == "<=" || value.left(2) == "<>")
{
// Check if we're filtering for '<> NULL'. In this case we need a special comparison operator.
if(value.left(2) == "<>" && value.mid(2) == "NULL")
{
// We are filtering for '<>NULL'. Override the comparison operator to search for NULL values in this column. Also treat search value (NULL) as number,
// in order to avoid putting quotes around it.
op = "IS NOT";
numeric = true;
val = "NULL";
} else if(value.left(2) == "<>" && value.mid(2) == "''") {
// We are filtering for "<>''", i.e. for everything which is not an empty string
op = "<>";
numeric = true;
val = "''";
} else {
value.midRef(2).toFloat(&numeric);
op = value.left(2);
val = value.mid(2);
}
} else if(value.left(1) == ">" || value.left(1) == "<") {
value.midRef(1).toFloat(&numeric);
op = value.at(0);
val = value.mid(1);
} else if(value.left(1) == "=") {
val = value.mid(1);
// Check if value to compare with is 'NULL'
if(val != "NULL")
{
// It's not, so just compare normally to the value, whatever it is.
op = "=";
} else {
// It is NULL. Override the comparison operator to search for NULL values in this column. Also treat search value (NULL) as number,
// in order to avoid putting quotes around it.
op = "IS";
numeric = true;
}
} else if(value.left(1) == "/" && value.right(1) == "/" && value.size() > 2) {
val = value.mid(1, value.length() - 2);
op = "REGEXP";
numeric = false;
} else {
// Keep the default LIKE operator
// Set the escape character if one has been specified in the settings dialog
QString escape_character = Settings::getValue("databrowser", "filter_escape").toString();
if(escape_character == "'") escape_character = "''";
if(escape_character.length())
escape = QString("ESCAPE '%1'").arg(escape_character);
// Add % wildcards at the start and at the beginning of the filter query, but only if there weren't set any
// wildcards manually. The idea is to assume that a user who's just typing characters expects the wildcards to
// be added but a user who adds them herself knows what she's doing and doesn't want us to mess up her query.
if(!value.contains("%"))
{
val = value;
val.prepend('%');
val.append('%');
}
}
}
if(val.isEmpty())
val = value;
if(val.isEmpty() || val == "%" || val == "%%")
return {};
else {
// Quote and escape value, but only if it's not numeric and not the empty string sequence
if(!numeric && val != "''")
val = sqlb::escapeString(val);
QString whereClause(op + " " + QString(encodeString(val.toUtf8(), encoding)));
if (!val2.isEmpty())
whereClause += " AND " + QString(encodeString(val2.toUtf8(), encoding));
whereClause += " " + escape;
return whereClause.toStdString();
}
}
Qt::AlignmentFlag CondFormat::alignmentFlag() const
{
switch (m_align) {
case AlignLeft:
return Qt::AlignLeft;
case AlignCenter:
return Qt::AlignCenter;
case AlignRight:
return Qt::AlignRight;
case AlignJustify:
return Qt::AlignJustify;
}
return Qt::AlignLeft;
}

View File

@ -0,0 +1,81 @@
#ifndef CONDFORMAT_H
#define CONDFORMAT_H
#include <QString>
#include <QColor>
#include <QFont>
#include <QModelIndex>
class QAbstractTableModel;
// Conditional formatting for given format to table cells based on a specified condition.
class CondFormat
{
public:
enum Alignment {
AlignLeft = 0,
AlignRight,
AlignCenter,
AlignJustify
};
// List of alignment texts. Order must be as Alignment definition above.
static QStringList alignmentTexts() {
return {QObject::tr("Left"), QObject::tr("Right"), QObject::tr("Center"), QObject::tr("Justify")};
}
// Get alignment from combined Qt alignment (note that this will lose any combination of our Alignment enum
// with other values present in the flag (e.g. vertical alignment).
static Alignment fromCombinedAlignment(Qt::Alignment align);
CondFormat() {}
CondFormat(const QString& filter,
const QColor& foreground,
const QColor& background,
const QFont& font,
const Alignment alignment = AlignLeft,
const QString& encoding = QString());
// Create a new CondFormat from values obtained from the model
CondFormat(const QString& filter,
const QAbstractTableModel* model,
const QModelIndex index,
const QString& encoding = QString());
static std::string filterToSqlCondition(const QString& value, const QString& encoding = QString());
private:
std::string m_sqlCondition;
QString m_filter;
QColor m_bgColor;
QColor m_fgColor;
QFont m_font;
Alignment m_align;
public:
std::string sqlCondition() const { return m_sqlCondition; }
QString filter() const { return m_filter; }
QColor backgroundColor() const { return m_bgColor; }
QColor foregroundColor() const { return m_fgColor; }
void setBackgroundColor(QColor color) { m_bgColor = color; }
void setForegroundColor(QColor color) { m_fgColor = color; }
bool isBold() const { return m_font.bold(); }
bool isItalic() const { return m_font.italic(); }
bool isUnderline() const { return m_font.underline(); }
void setBold(bool value) { m_font.setBold(value); }
void setItalic(bool value) { m_font.setItalic(value); }
void setUnderline(bool value) { m_font.setUnderline(value); }
QFont font() const { return m_font; }
void setFontFamily(const QString &family) { m_font.setFamily(family); }
void setFontPointSize(int pointSize) { m_font.setPointSize(pointSize); }
Alignment alignment() const { return m_align; }
void setAlignment(Alignment value) { m_align = value; }
Qt::AlignmentFlag alignmentFlag() const;
};
#endif // CONDFORMAT_H

View File

@ -0,0 +1,252 @@
#include "CondFormatManager.h"
#include "ui_CondFormatManager.h"
#include "CondFormat.h"
#include "Settings.h"
#include "FilterLineEdit.h"
#include <QColorDialog>
#include <QDesktopServices>
#include <QUrl>
#include <QPushButton>
#include <QMessageBox>
#include <QFontComboBox>
#include <QSpinBox>
#include <QComboBox>
#include <QStyledItemDelegate>
// Styled Item Delegate for non-editable columns (all except Condition)
class DefaultDelegate: public QStyledItemDelegate {
public:
explicit DefaultDelegate(QObject* parent=nullptr): QStyledItemDelegate(parent) {}
QWidget* createEditor(QWidget* /* parent */, const QStyleOptionViewItem& /* option */, const QModelIndex& /* index */) const override {
return nullptr;
}
};
// Styled Item Delegate for the Condition column as a filter line editor.
class FilterEditDelegate: public QStyledItemDelegate {
public:
explicit FilterEditDelegate(QObject* parent=nullptr): QStyledItemDelegate(parent) {}
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem& /* option */, const QModelIndex& /* index */) const override {
FilterLineEdit* filterEditor = new FilterLineEdit(parent);
filterEditor->setConditionFormatContextMenuEnabled(false);
return filterEditor;
}
};
CondFormatManager::CondFormatManager(const std::vector<CondFormat>& condFormats, const QString& encoding, QWidget *parent) :
QDialog(parent),
ui(new Ui::CondFormatManager),
m_condFormats(condFormats),
m_encoding(encoding)
{
ui->setupUi(this);
for(const CondFormat& aCondFormat : condFormats)
addItem(aCondFormat);
// Make condition editable as a filter line editor.
ui->tableCondFormats->setEditTriggers(QAbstractItemView::AllEditTriggers);
ui->tableCondFormats->setItemDelegateForColumn(ColumnFilter, new FilterEditDelegate(this));
// Make columns not text-editable, except for the condition.
for(int col = ColumnForeground; col < ColumnFilter; ++col) {
ui->tableCondFormats->setItemDelegateForColumn(col, new DefaultDelegate(this));
ui->tableCondFormats->resizeColumnToContents(col);
}
connect(ui->buttonAdd, &QToolButton::clicked, this, &CondFormatManager::addNewItem);
connect(ui->buttonRemove, &QToolButton::clicked, this, &CondFormatManager::removeItem);
connect(ui->buttonDown, &QToolButton::clicked, this, &CondFormatManager::downItem);
connect(ui->buttonUp, &QToolButton::clicked, this, &CondFormatManager::upItem);
connect(ui->tableCondFormats, &QTreeWidget::itemClicked, this, &CondFormatManager::itemClicked);
}
CondFormatManager::~CondFormatManager()
{
delete ui;
}
void CondFormatManager::addNewItem()
{
// Clear focus from the current widget, so if there is some input, it is saved now.
QWidget* currentWidget = ui->tableCondFormats->itemWidget(ui->tableCondFormats->currentItem(), ColumnFilter);
if (currentWidget != nullptr)
currentWidget->clearFocus();
QFont font = QFont(Settings::getValue("databrowser", "font").toString());
font.setPointSize(Settings::getValue("databrowser", "fontsize").toInt());
CondFormat newCondFormat("", QColor(Settings::getValue("databrowser", "reg_fg_colour").toString()),
m_condFormatPalette.nextSerialColor(Palette::appHasDarkTheme()),
font,
CondFormat::AlignLeft,
m_encoding);
addItem(newCondFormat);
}
void CondFormatManager::addItem(const CondFormat& aCondFormat)
{
int i = ui->tableCondFormats->topLevelItemCount();
QTreeWidgetItem *newItem = new QTreeWidgetItem(ui->tableCondFormats);
newItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable);
newItem->setForeground(ColumnForeground, aCondFormat.foregroundColor());
newItem->setBackground(ColumnForeground, aCondFormat.foregroundColor());
newItem->setForeground(ColumnBackground, aCondFormat.backgroundColor());
newItem->setBackground(ColumnBackground, aCondFormat.backgroundColor());
newItem->setToolTip(ColumnBackground, tr("Click to select color"));
newItem->setToolTip(ColumnForeground, tr("Click to select color"));
QFontComboBox* fontCombo = new QFontComboBox(ui->tableCondFormats);
fontCombo->setCurrentFont(aCondFormat.font());
// Avoid that the font combo box gets too wide and work around a possible Qt bug where the box overlaps the next column.
fontCombo->setMaximumWidth(150);
ui->tableCondFormats->setItemWidget(newItem, ColumnFont, fontCombo);
QSpinBox* sizeBox = new QSpinBox(ui->tableCondFormats);
sizeBox->setMinimum(1);
sizeBox->setValue(aCondFormat.font().pointSize());
ui->tableCondFormats->setItemWidget(newItem, ColumnSize, sizeBox);
newItem->setCheckState(ColumnBold, aCondFormat.isBold() ? Qt::Checked : Qt::Unchecked);
newItem->setCheckState(ColumnItalic, aCondFormat.isItalic() ? Qt::Checked : Qt::Unchecked);
newItem->setCheckState(ColumnUnderline, aCondFormat.isUnderline() ? Qt::Checked : Qt::Unchecked);
QComboBox* alignCombo = new QComboBox(ui->tableCondFormats);
alignCombo->addItems(CondFormat::alignmentTexts());
alignCombo->setCurrentIndex(aCondFormat.alignment());
ui->tableCondFormats->setItemWidget(newItem, ColumnAlignment, alignCombo);
newItem->setText(ColumnFilter, aCondFormat.filter());
ui->tableCondFormats->insertTopLevelItem(i, newItem);
}
void CondFormatManager::removeItem()
{
QTreeWidgetItem* item = ui->tableCondFormats->takeTopLevelItem(ui->tableCondFormats->currentIndex().row());
delete item;
}
void CondFormatManager::moveItem(int offset)
{
if (!ui->tableCondFormats->currentIndex().isValid())
return;
int selectedRow = ui->tableCondFormats->currentIndex().row();
int newRow = selectedRow + offset;
if(newRow < 0 || newRow >= ui->tableCondFormats->topLevelItemCount())
return;
QTreeWidgetItem* item = ui->tableCondFormats->topLevelItem(selectedRow);
// Rescue widgets, since they will be deleted, and add them later.
QFontComboBox* fontCombo = qobject_cast<QFontComboBox*>(ui->tableCondFormats->itemWidget(item, ColumnFont));
QFontComboBox* fontCombo2 = new QFontComboBox(ui->tableCondFormats);
fontCombo2->setCurrentFont(fontCombo->currentFont());
QSpinBox* sizeBox = qobject_cast<QSpinBox*>(ui->tableCondFormats->itemWidget(item, ColumnSize));
QSpinBox* sizeBox2 = new QSpinBox(ui->tableCondFormats);
sizeBox2->setValue(sizeBox->value());
sizeBox2->setMinimum(sizeBox->minimum());
QComboBox* alignCombo = qobject_cast<QComboBox*>(ui->tableCondFormats->itemWidget(item, ColumnAlignment));
QComboBox* alignCombo2 = new QComboBox(ui->tableCondFormats);
alignCombo2->addItems(CondFormat::alignmentTexts());
alignCombo2->setCurrentIndex(alignCombo->currentIndex());
item = ui->tableCondFormats->takeTopLevelItem(selectedRow);
ui->tableCondFormats->insertTopLevelItem(newRow, item);
// Restore widgets and state
ui->tableCondFormats->setItemWidget(item, ColumnFont, fontCombo2);
ui->tableCondFormats->setItemWidget(item, ColumnSize, sizeBox2);
ui->tableCondFormats->setItemWidget(item, ColumnAlignment, alignCombo2);
ui->tableCondFormats->setCurrentIndex(ui->tableCondFormats->currentIndex().sibling(newRow,
ui->tableCondFormats->currentIndex().column()));
// This is added so the widgets are readjusted. Otherwise they can be misplaced when moving an item to the top.
ui->tableCondFormats->adjustSize();
}
void CondFormatManager::upItem()
{
moveItem(-1);
}
void CondFormatManager::downItem()
{
moveItem(+1);
}
std::vector<CondFormat> CondFormatManager::getCondFormats() const
{
std::vector<CondFormat> result;
for (int i = 0; i < ui->tableCondFormats->topLevelItemCount(); ++i)
{
QTreeWidgetItem* item = ui->tableCondFormats->topLevelItem(i);
QFontComboBox* fontCombo = qobject_cast<QFontComboBox*>(ui->tableCondFormats->itemWidget(item, ColumnFont));
QSpinBox* sizeBox = qobject_cast<QSpinBox*>(ui->tableCondFormats->itemWidget(item, ColumnSize));
QFont font = fontCombo->currentFont();
font.setPointSize(sizeBox->value());
font.setBold(item->checkState(ColumnBold) == Qt::Checked);
font.setItalic(item->checkState(ColumnItalic) == Qt::Checked);
font.setUnderline(item->checkState(ColumnUnderline) == Qt::Checked);
QComboBox* alignCombo = qobject_cast<QComboBox*>(ui->tableCondFormats->itemWidget(item, ColumnAlignment));
result.emplace_back(item->text(ColumnFilter),
item->background(ColumnForeground).color(),
item->background(ColumnBackground).color(),
font,
static_cast<CondFormat::Alignment>(alignCombo->currentIndex()),
m_encoding);
}
return result;
}
void CondFormatManager::itemClicked(QTreeWidgetItem* item, int column)
{
switch (column) {
case ColumnForeground:
case ColumnBackground: {
QColor color = QColorDialog::getColor(item->background(column).color(), this);
if(color.isValid()) {
item->setTextColor(column, color);
item->setBackgroundColor(column, color);
}
break;
}
case ColumnBold:
case ColumnItalic:
case ColumnUnderline:
item->setCheckState(column, item->checkState(column) != Qt::Checked ? Qt::Checked : Qt::Unchecked);
break;
default:
// Nothing to do
break;
}
}
void CondFormatManager::on_buttonBox_clicked(QAbstractButton* button)
{
if (button == ui->buttonBox->button(QDialogButtonBox::Cancel))
reject();
else if (button == ui->buttonBox->button(QDialogButtonBox::Ok))
accept();
else if (button == ui->buttonBox->button(QDialogButtonBox::Help))
QDesktopServices::openUrl(QUrl("https://github.com/sqlitebrowser/sqlitebrowser/wiki/Conditional-Formats"));
else if (button == ui->buttonBox->button(QDialogButtonBox::Reset)) {
if (QMessageBox::warning(this,
QApplication::applicationName(),
tr("Are you sure you want to clear all the conditional formats of this field?"),
QMessageBox::Reset | QMessageBox::Cancel,
QMessageBox::Cancel) == QMessageBox::Reset)
if(ui->tableCondFormats->model()->hasChildren())
ui->tableCondFormats->model()->removeRows(0, ui->tableCondFormats->model()->rowCount());
}
}

View File

@ -0,0 +1,56 @@
#ifndef CONDFORMATMANAGER_H
#define CONDFORMATMANAGER_H
#include "WBFZExchangePluginAPI.h"
#include <QDialog>
#include <vector>
#include "Palette.h"
namespace Ui {
class CondFormatManager;
}
class CondFormat;
class QTreeWidgetItem;
class QAbstractButton;
class CondFormatManager : public QDialog
{
Q_OBJECT
public:
explicit CondFormatManager(const std::vector<CondFormat>& condFormats, const QString& encoding, QWidget *parent = nullptr);
~CondFormatManager() override;
std::vector<CondFormat> getCondFormats() const;
private:
enum Columns {
ColumnForeground = 0,
ColumnBackground,
ColumnFont,
ColumnSize,
ColumnBold,
ColumnItalic,
ColumnUnderline,
ColumnAlignment,
ColumnFilter
};
Ui::CondFormatManager *ui;
std::vector<CondFormat> m_condFormats;
Palette m_condFormatPalette;
QString m_encoding;
private slots:
void addNewItem();
void addItem(const CondFormat& aCondFormat);
void removeItem();
void moveItem(int offset);
void upItem();
void downItem();
void on_buttonBox_clicked(QAbstractButton* button);
public slots:
void itemClicked(QTreeWidgetItem* item, int column);
};
#endif // CONDFORMATMANAGER_H

View File

@ -0,0 +1,279 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CondFormatManager</class>
<widget class="QDialog" name="CondFormatManager">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>750</width>
<height>400</height>
</rect>
</property>
<property name="windowTitle">
<string>Conditional Format Manager</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="labelTitle">
<property name="text">
<string>This dialog allows creating and editing conditional formats. Each cell style will be selected by the first accomplished condition for that cell data. Conditional formats can be moved up and down, where those at higher rows take precedence over those at lower. Syntax for conditions is the same as for filters and an empty condition applies to all values.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QToolButton" name="buttonAdd">
<property name="toolTip">
<string>Add new conditional format</string>
</property>
<property name="text">
<string>&amp;Add</string>
</property>
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/field_add</normaloff>:/icons/field_add</iconset>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="buttonRemove">
<property name="toolTip">
<string>Remove selected conditional format</string>
</property>
<property name="text">
<string>&amp;Remove</string>
</property>
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/field_delete</normaloff>:/icons/field_delete</iconset>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="buttonUp">
<property name="toolTip">
<string>Move selected conditional format up</string>
</property>
<property name="text">
<string>Move &amp;up</string>
</property>
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/up</normaloff>:/icons/up</iconset>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="buttonDown">
<property name="toolTip">
<string>Move selected conditional format down</string>
</property>
<property name="text">
<string>Move &amp;down</string>
</property>
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/down</normaloff>:/icons/down</iconset>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QTreeWidget" name="tableCondFormats">
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
<property name="editTriggers">
<set>QAbstractItemView::AllEditTriggers</set>
</property>
<property name="tabKeyNavigation">
<bool>true</bool>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectItems</enum>
</property>
<property name="indentation">
<number>0</number>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<property name="uniformRowHeights">
<bool>true</bool>
</property>
<property name="itemsExpandable">
<bool>false</bool>
</property>
<property name="expandsOnDoubleClick">
<bool>false</bool>
</property>
<attribute name="headerMinimumSectionSize">
<number>25</number>
</attribute>
<column>
<property name="text">
<string>Foreground</string>
</property>
<property name="toolTip">
<string>Text color</string>
</property>
</column>
<column>
<property name="text">
<string>Background</string>
</property>
<property name="toolTip">
<string>Background color</string>
</property>
</column>
<column>
<property name="text">
<string>Font</string>
</property>
</column>
<column>
<property name="text">
<string>Size</string>
</property>
</column>
<column>
<property name="text">
<string/>
</property>
<property name="toolTip">
<string>Bold</string>
</property>
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/text_bold.png</normaloff>:/icons/text_bold.png</iconset>
</property>
</column>
<column>
<property name="text">
<string/>
</property>
<property name="toolTip">
<string>Italic</string>
</property>
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/text_italic.png</normaloff>:/icons/text_italic.png</iconset>
</property>
</column>
<column>
<property name="text">
<string/>
</property>
<property name="toolTip">
<string>Underline</string>
</property>
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/text_underline.png</normaloff>:/icons/text_underline.png</iconset>
</property>
</column>
<column>
<property name="text">
<string>Alignment</string>
</property>
</column>
<column>
<property name="text">
<string>Condition</string>
</property>
</column>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok|QDialogButtonBox::Reset</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="icons/icons.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>CondFormatManager</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>CondFormatManager</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,157 @@
#include "Data.h"
#include <QBuffer>
#include <QDateTime>
#include <QImageReader>
#include <QLocale>
#include <QTextCodec>
#include <algorithm>
// Note that these aren't all possible BOMs. But they are probably the most common ones.
// The size is needed at least for the ones with character zero in them.
static const QByteArray bom3("\xEF\xBB\xBF", 3);
static const QByteArray bom2a("\xFE\xFF", 2);
static const QByteArray bom2b("\xFF\xFE", 2);
static const QByteArray bom4a("\x00\x00\xFE\xFF", 4);
static const QByteArray bom4b("\xFF\xFE\x00\x00", 4);
bool isTextOnly(QByteArray data, const QString& encoding, bool quickTest)
{
// If the data starts with a Unicode BOM, we can use detection provided by QTextCodec.
if(startsWithBom(data)) {
QTextCodec *codec = encoding.isEmpty()? QTextCodec::codecForName("UTF-8") : QTextCodec::codecForName(encoding.toUtf8());
QTextCodec *detectedCodec = QTextCodec::codecForUtfText(data, nullptr);
return detectedCodec == codec;
}
// Truncate to the first few bytes for quick testing
int testSize = quickTest? std::min(512, data.size()) : data.size();
// If the quick test has been requested and we have to truncate the string, we have to use
// an approach where truncated multibyte characters are not interpreted as invalid characters.
if(quickTest && data.size() > testSize) {
// We can assume that the default encoding (UTF-8) and all the ISO-8859
// cannot contain character zero.
// This has to be checked explicitly because toUnicode() is using zero as
// a terminator for these encodings.
if((encoding.isEmpty() || encoding.startsWith("ISO-8859")) && data.contains('\0'))
return false;
QTextCodec::ConverterState state;
QTextCodec *codec = encoding.isEmpty()? QTextCodec::codecForName("UTF-8") : QTextCodec::codecForName(encoding.toUtf8());
codec->toUnicode(data.constData(), testSize, &state);
return state.invalidChars == 0;
} else {
// Convert to Unicode if necessary
data = decodeString(data, encoding);
// Perform check
return QString(data).toUtf8() == data;
}
}
bool containsRightToLeft(const QString& text) {
for(QChar ch : text) {
switch(ch.direction()) {
case QChar::DirR:
case QChar::DirAL:
case QChar::DirRLE:
case QChar::DirRLO:
case QChar::DirRLI:
return true;
default:
break;
}
}
return false;
}
bool startsWithBom(const QByteArray& data)
{
if(data.startsWith(bom3) ||
data.startsWith(bom2a) || data.startsWith(bom2b) ||
data.startsWith(bom4a) || data.startsWith(bom4b))
return true;
else
return false;
}
QByteArray removeBom(QByteArray& data)
{
if(data.startsWith(bom3))
{
QByteArray bom = data.left(3);
data.remove(0, 3);
return bom;
} else if(data.startsWith(bom2a) || data.startsWith(bom2b)) {
QByteArray bom = data.left(2);
data.remove(0, 2);
return bom;
} else if(data.startsWith(bom4a) || data.startsWith(bom4b)) {
QByteArray bom = data.left(4);
data.remove(0, 4);
return bom;
} else {
return QByteArray();
}
}
QString isImageData(const QByteArray& data)
{
// 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(const_cast<QByteArray*>(&data));
QImageReader readerBuffer(&imageBuffer);
QString imageFormat = readerBuffer.format();
if(readerBuffer.canRead() && !readerBuffer.read().isNull())
return imageFormat;
else
return QString();
}
QStringList toStringList(const QList<QByteArray>& list) {
QStringList strings;
for (const QByteArray &item : list) {
strings.append(QString::fromUtf8(item));
}
return strings;
}
QByteArray encodeString(const QByteArray& str, const QString& encoding)
{
if(encoding.isEmpty())
return str;
else
return QTextCodec::codecForName(encoding.toUtf8())->fromUnicode(str);
}
QByteArray decodeString(const QByteArray& str, const QString& encoding)
{
if(encoding.isEmpty())
return str;
else
return QTextCodec::codecForName(encoding.toUtf8())->toUnicode(str).toUtf8();
}
QString humanReadableSize(unsigned long byteCount)
{
static const std::vector<QString> units = {"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB"};
double size = static_cast<double>(byteCount);
for(const QString& unit : units)
{
if(size < 1024.0)
return QString::number(size, 'f', 2) + " " + unit;
size /= 1024.0;
}
return QString::number(size, 'f', 2) + " YiB";
}
QString isoDateTimeStringToLocalDateTimeString(const QString& date_string)
{
return QLocale::system().toString(QDateTime::fromString(date_string, Qt::ISODate).toLocalTime(), QLocale::ShortFormat);
}

View File

@ -0,0 +1,37 @@
#ifndef DATA_H
#define DATA_H
#include <QString>
#include <QByteArray>
// This returns false if the data in the data parameter contains binary data. If it is text only, the function returns
// true. If the second parameter is specified, it will be used to convert the data from the given encoding to Unicode
// before doing the check. The third parameter can be used to only check the first couple of bytes which speeds up the
// text but makes it less reliable
bool isTextOnly(QByteArray data, const QString& encoding = QString(), bool quickTest = false);
// This returns true if text contains some character whose direction is right-to-left.
bool containsRightToLeft(const QString& text);
// This function returns true if the data in the data parameter starts with a Unicode BOM. Otherwise it returns false.
bool startsWithBom(const QByteArray& data);
// This function checks if the data in the data parameter starts with a Unicode BOM. If so, the BOM is removed from the
// byte array and passed back to the caller separately as the return value of the function. If the data does not start
// with a BOM an empty byte array is returned and the original data is not modified.
QByteArray removeBom(QByteArray& data);
// Check if a byte array contains an image. Returns the name of the image format for images or a null string for non-image data.
QString isImageData(const QByteArray& data);
QStringList toStringList(const QList<QByteArray>& list);
QByteArray encodeString(const QByteArray& str, const QString& encoding);
QByteArray decodeString(const QByteArray& str, const QString& encoding);
QString humanReadableSize(unsigned long byteCount);
QString isoDateTimeStringToLocalDateTimeString(const QString& date_string);
#endif

View File

@ -0,0 +1,429 @@
#include "DbStructureModel.h"
#include "IconCache.h"
#include "sqlitedb.h"
#include "sqlitetablemodel.h"
#include "Settings.h"
#include <QTreeWidgetItem>
#include <QMimeData>
#include <QMessageBox>
#include <QApplication>
#include <unordered_map>
#include <map>
DbStructureModel::DbStructureModel(DBBrowserDB& db, QObject* parent)
: QAbstractItemModel(parent),
m_db(db),
browsablesRootItem(nullptr),
m_dropQualifiedNames(false),
m_dropEnquotedNames(false)
{
// Create root item and use its columns to store the header strings
QStringList header;
header << tr("Name") << tr("Object") << tr("Type") << tr("Schema") << tr("Database");
rootItem = new QTreeWidgetItem(header);
}
DbStructureModel::~DbStructureModel()
{
delete rootItem;
}
int DbStructureModel::columnCount(const QModelIndex&) const
{
return rootItem->columnCount();
}
QVariant DbStructureModel::data(const QModelIndex& index, int role) const
{
if(!index.isValid())
return QVariant();
// Get the item the index points at
QTreeWidgetItem* item = static_cast<QTreeWidgetItem*>(index.internalPointer());
// Depending on the role either return the text or the icon
switch(role)
{
case Qt::DisplayRole:
// For the display role and the browsable branch of the tree we want to show the column name including the schema name if necessary (i.e.
// for schemata != "main"). For the normal structure branch of the tree we don't want to add the schema name because it's already obvious from
// the position of the item in the tree.
if(index.column() == ColumnName && item->parent() == browsablesRootItem)
return QString::fromStdString(sqlb::ObjectIdentifier(item->text(ColumnSchema).toStdString(), item->text(ColumnName).toStdString()).toDisplayString());
else
return Settings::getValue("db", "hideschemalinebreaks").toBool() ? item->text(index.column()).simplified() : item->text(index.column());
case Qt::EditRole:
return item->text(index.column());
case Qt::ToolTipRole: {
// Show the original text but limited, when it's supposed to be shown in a tooltip
QString text = item->text(index.column());
if (text.length() > 512) {
text.truncate(509);
text.append("...");
}
return text;
}
case Qt::DecorationRole:
return item->icon(index.column());
default:
return QVariant();
}
}
Qt::ItemFlags DbStructureModel::flags(const QModelIndex &index) const
{
if(!index.isValid())
return Qt::ItemIsDropEnabled;
// All items are enabled and selectable
Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled;
// Only enable dragging for SQLite objects and for fields (composition in SQL text editor).
// Grouping nodes have no type.
QString type = data(index.sibling(index.row(), ColumnObjectType), Qt::DisplayRole).toString();
if(!type.isEmpty())
flags |= Qt::ItemIsDragEnabled;
return flags;
}
QVariant DbStructureModel::headerData(int section, Qt::Orientation orientation, int role) const
{
// Get the header string from the root item
if(orientation == Qt::Horizontal && role == Qt::DisplayRole)
return rootItem->data(section, role);
return QVariant();
}
QModelIndex DbStructureModel::index(int row, int column, const QModelIndex& parent) const
{
if(!hasIndex(row, column, parent))
return QModelIndex();
QTreeWidgetItem *parentItem;
if(!parent.isValid())
parentItem = rootItem;
else
parentItem = static_cast<QTreeWidgetItem*>(parent.internalPointer());
QTreeWidgetItem* childItem = parentItem->child(row);
if(childItem)
return createIndex(row, column, childItem);
else
return QModelIndex();
}
QModelIndex DbStructureModel::parent(const QModelIndex& index) const
{
if(!index.isValid())
return QModelIndex();
QTreeWidgetItem* childItem = static_cast<QTreeWidgetItem*>(index.internalPointer());
QTreeWidgetItem* parentItem = childItem->parent();
if(parentItem == rootItem)
return QModelIndex();
else
return createIndex(0, 0, parentItem);
}
int DbStructureModel::rowCount(const QModelIndex& parent) const
{
if(parent.column() > 0)
return 0;
if(!parent.isValid())
return rootItem->childCount();
else
return static_cast<QTreeWidgetItem*>(parent.internalPointer())->childCount();
}
void DbStructureModel::reloadData()
{
beginResetModel();
// Remove all data except for the root item
while(rootItem->childCount())
delete rootItem->child(0);
// Return here if no DB is opened
if(!m_db.isOpen())
{
endResetModel();
emit structureUpdated();
return;
}
// Create the nodes for browsables and for tables, indices, views and triggers. The idea here is to basically have two trees in one model:
// In the root node there are two nodes: 'browsables' and 'all'. The first node contains a list of all browsable objects, i.e. views and tables.
// The second node contains four sub-nodes (tables, indices, views and triggers), each containing a list of objects of that type.
// This way we only have to have and only have to update one model and can use it in all sorts of places, just by setting a different root node.
browsablesRootItem = new QTreeWidgetItem(rootItem);
browsablesRootItem->setIcon(ColumnName, IconCache::get("view"));
browsablesRootItem->setText(ColumnName, tr("Browsables"));
// Make sure to always load the main schema first
QTreeWidgetItem* itemAll = new QTreeWidgetItem(rootItem);
itemAll->setIcon(ColumnName, IconCache::get("database"));
itemAll->setText(ColumnName, tr("All"));
itemAll->setText(ColumnObjectType, "database");
buildTree(itemAll, "main");
// Add the temporary database as a node if it isn't empty. Make sure it's always second if it exists.
if(!m_db.schemata["temp"].empty())
{
QTreeWidgetItem* itemTemp = new QTreeWidgetItem(itemAll);
itemTemp->setIcon(ColumnName, IconCache::get("database"));
itemTemp->setText(ColumnName, tr("Temporary"));
itemTemp->setText(ColumnObjectType, "database");
buildTree(itemTemp, "temp");
}
// Now load all the other schemata last
for(const auto& it : m_db.schemata)
{
// Don't load the main and temp schema again
if(it.first != "main" && it.first != "temp")
{
QTreeWidgetItem* itemSchema = new QTreeWidgetItem(itemAll);
itemSchema->setIcon(ColumnName, IconCache::get("database"));
itemSchema->setText(ColumnName, QString::fromStdString(it.first));
itemSchema->setText(ColumnObjectType, "database");
buildTree(itemSchema, it.first);
}
}
// Refresh the view
endResetModel();
emit structureUpdated();
}
QStringList DbStructureModel::mimeTypes() const
{
QStringList types;
types << "text/plain";
return types;
}
QMimeData* DbStructureModel::mimeData(const QModelIndexList& indices) const
{
// We store the SQL data and the names data separately
QByteArray sqlData, namesData;
// Loop through selected indices
for(const QModelIndex& index : indices)
{
// Get the item the index points at
QTreeWidgetItem* item = static_cast<QTreeWidgetItem*>(index.internalPointer());
// Only export data for valid indices and only once per row (SQL column or Name column).
if(index.isValid()) {
QString objectType = data(index.sibling(index.row(), ColumnObjectType), Qt::DisplayRole).toString();
// For names, export a (qualified) (escaped) identifier of the item for statement composition in SQL editor.
if(objectType == "field")
namesData.append(getNameForDropping(item->text(ColumnSchema), item->parent()->text(ColumnName), item->text(ColumnName)));
else if(objectType == "database")
namesData.append(getNameForDropping(item->text(ColumnName), "", ""));
else if(!objectType.isEmpty())
namesData.append(getNameForDropping(item->text(ColumnSchema), item->text(ColumnName), ""));
if(objectType != "field" && index.column() == ColumnSQL)
{
// Add the SQL code used to create the object
sqlData.append(data(index, Qt::DisplayRole).toString() + ";\n");
// If it is a table also add the content
if(objectType == "table")
{
SqliteTableModel tableModel(m_db);
sqlb::ObjectIdentifier objid(data(index.sibling(index.row(), ColumnSchema), Qt::DisplayRole).toString().toStdString(),
data(index.sibling(index.row(), ColumnName), Qt::DisplayRole).toString().toStdString());
tableModel.setQuery(sqlb::Query(objid));
if(tableModel.completeCache())
{
// Only continue if all data was fetched
for(int i=0; i < tableModel.rowCount(); ++i)
{
QString insertStatement = "INSERT INTO " + QString::fromStdString(objid.toString()) + " VALUES(";
for(int j=1; j < tableModel.columnCount(); ++j)
insertStatement += QString("'%1',").arg(tableModel.data(tableModel.index(i, j), Qt::EditRole).toString());
insertStatement.chop(1);
insertStatement += ");\n";
sqlData.append(insertStatement);
}
}
}
}
}
}
// Create the MIME data object
QMimeData* mime = new QMimeData();
mime->setProperty("db_file", m_db.currentFile()); // Also save the file name to avoid dropping an object on the same database as it comes from
// When we have both SQL and Names data (probable row selection mode) we give precedence to the SQL data
if (sqlData.length() == 0 && namesData.length() > 0) {
// Remove last ", " or "."
if (namesData.endsWith(", "))
namesData.chop(2);
else if (namesData.endsWith("."))
namesData.chop(1);
mime->setData("text/plain", namesData);
} else
mime->setData("text/plain", sqlData);
return mime;
}
bool DbStructureModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int, int, const QModelIndex&)
{
if(action == Qt::IgnoreAction)
return true;
if(!data->hasFormat("text/plain"))
return false;
if(data->property("db_file") == m_db.currentFile())
return false;
// Get data
QByteArray d = data->data("text/plain");
// Try to execute the SQL statement
if(m_db.executeMultiSQL(d, true, true))
{
m_db.updateSchema();
return true;
} else {
QMessageBox::warning(nullptr, QApplication::applicationName(), m_db.lastError());
return false;
}
}
static long calc_number_of_objects_by_type(const objectMap& objmap, const std::string& type)
{
auto objects = objmap.equal_range(type);
if(objects.first == objmap.end())
return 0;
else
return std::distance(objects.first, objects.second);
}
void DbStructureModel::buildTree(QTreeWidgetItem* parent, const std::string& schema)
{
// Build a map from object type to tree node to simplify finding the correct tree node later
std::unordered_map<std::string, QTreeWidgetItem*> typeToParentItem;
// Get object map for the given schema
objectMap objmap = m_db.schemata[schema];
// Prepare tree
QTreeWidgetItem* itemTables = new QTreeWidgetItem(parent);
itemTables->setIcon(ColumnName, IconCache::get("table"));
itemTables->setText(ColumnName, tr("Tables (%1)").arg(calc_number_of_objects_by_type(objmap, "table")));
typeToParentItem.insert({"table", itemTables});
QTreeWidgetItem* itemIndices = new QTreeWidgetItem(parent);
itemIndices->setIcon(ColumnName, IconCache::get("index"));
itemIndices->setText(ColumnName, tr("Indices (%1)").arg(calc_number_of_objects_by_type(objmap, "index")));
typeToParentItem.insert({"index", itemIndices});
QTreeWidgetItem* itemViews = new QTreeWidgetItem(parent);
itemViews->setIcon(ColumnName, IconCache::get("view"));
itemViews->setText(ColumnName, tr("Views (%1)").arg(calc_number_of_objects_by_type(objmap, "view")));
typeToParentItem.insert({"view", itemViews});
QTreeWidgetItem* itemTriggers = new QTreeWidgetItem(parent);
itemTriggers->setIcon(ColumnName, IconCache::get("trigger"));
itemTriggers->setText(ColumnName, tr("Triggers (%1)").arg(calc_number_of_objects_by_type(objmap, "trigger")));
typeToParentItem.insert({"trigger", itemTriggers});
// Get all database objects and sort them by their name.
// This needs to be a multimap because SQLite allows views and triggers with the same name which means that names can appear twice.
std::multimap<std::string, sqlb::ObjectPtr> dbobjs;
for(const auto& it : objmap)
dbobjs.insert({it.second->name(), it.second});
// Add the database objects to the tree nodes
for(const auto& obj : dbobjs)
{
sqlb::ObjectPtr it = obj.second;
// Object node
QTreeWidgetItem* item = addNode(typeToParentItem.at(sqlb::Object::typeToString(it->type())), it, schema);
// If it is a table or view add the field nodes, add an extra node for the browsable section
if(it->type() == sqlb::Object::Types::Table || it->type() == sqlb::Object::Types::View)
addNode(browsablesRootItem, it, schema);
// Add field nodes if there are any
sqlb::FieldInfoList fieldList = it->fieldInformation();
if(!fieldList.empty())
{
sqlb::StringVector pk_columns;
if(it->type() == sqlb::Object::Types::Table)
{
const auto pk = std::dynamic_pointer_cast<sqlb::Table>(it)->primaryKey();
if(pk)
pk_columns = pk->columnList();
}
for(const sqlb::FieldInfo& field : fieldList)
{
QTreeWidgetItem *fldItem = new QTreeWidgetItem(item);
bool isFK = false;
if(it->type() == sqlb::Object::Types::Table)
isFK = std::dynamic_pointer_cast<sqlb::Table>(it)->constraint({field.name}, sqlb::Constraint::ForeignKeyConstraintType) != nullptr;
fldItem->setText(ColumnName, QString::fromStdString(field.name));
fldItem->setText(ColumnObjectType, "field");
fldItem->setText(ColumnDataType, QString::fromStdString(field.type));
fldItem->setText(ColumnSQL, QString::fromStdString(field.sql));
fldItem->setText(ColumnSchema, QString::fromStdString(schema));
if(contains(pk_columns, field.name))
fldItem->setIcon(ColumnName, IconCache::get("field_key"));
else if(isFK)
fldItem->setIcon(ColumnName, IconCache::get("field_fk"));
else
fldItem->setIcon(ColumnName, IconCache::get("field"));
}
}
}
}
QTreeWidgetItem* DbStructureModel::addNode(QTreeWidgetItem* parent, const sqlb::ObjectPtr& object, const std::string& schema)
{
std::string type = sqlb::Object::typeToString(object->type());
QTreeWidgetItem *item = new QTreeWidgetItem(parent);
item->setIcon(ColumnName, IconCache::get(type));
item->setText(ColumnName, QString::fromStdString(object->name()));
item->setText(ColumnObjectType, QString::fromStdString(type));
item->setText(ColumnSQL, QString::fromStdString(object->originalSql()));
item->setText(ColumnSchema, QString::fromStdString(schema));
return item;
}
QString DbStructureModel::getNameForDropping(const QString& domain, const QString& object, const QString& field) const
{
// Take into account the drag&drop options for composing a name. Commas are included for composing a
// list of fields. A dot is added after other items, in order to allow composing a fully qualified name
// by selecting together and dropping a parent item and a child (e.g. select with control an attached
// database and a table, and drag and drop them to get "schema1"."table1".) Note that this only makes
// sense when the "Drop Qualified Names" option is not set.
QString name;
if ((domain != "main" && m_dropQualifiedNames) || object.isEmpty())
name = m_dropEnquotedNames ? sqlb::escapeIdentifier(domain) + "." : domain + ".";
if (!object.isEmpty() && (m_dropQualifiedNames || field.isEmpty()))
name += m_dropEnquotedNames ? sqlb::escapeIdentifier(object) + "." : object + ".";
if (!field.isEmpty())
name += m_dropEnquotedNames ? sqlb::escapeIdentifier(field) + ", " : field + ", ";
return name;
}

View File

@ -0,0 +1,60 @@
#ifndef DBSTRUCTUREMODEL_H
#define DBSTRUCTUREMODEL_H
#include "WBFZExchangePluginAPI.h"
#include <QAbstractItemModel>
#include <memory>
class DBBrowserDB;
class QTreeWidgetItem;
namespace sqlb { class Object; using ObjectPtr = std::shared_ptr<Object>; }
class DbStructureModel : public QAbstractItemModel
{
Q_OBJECT
public:
explicit DbStructureModel(DBBrowserDB& db, QObject* parent = nullptr);
~DbStructureModel() override;
QVariant data(const QModelIndex& index, int role) const override;
Qt::ItemFlags flags(const QModelIndex& index) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex& index) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex& = QModelIndex()) const override;
QStringList mimeTypes() const override;
QMimeData* mimeData(const QModelIndexList& indices) const override;
bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) override;
enum Columns
{
ColumnName,
ColumnObjectType,
ColumnDataType,
ColumnSQL,
ColumnSchema,
};
public slots:
void reloadData();
void setDropQualifiedNames(bool value) { m_dropQualifiedNames = value; }
void setDropEnquotedNames(bool value) { m_dropEnquotedNames = value; }
signals:
void structureUpdated();
private:
DBBrowserDB& m_db;
QTreeWidgetItem* rootItem;
QTreeWidgetItem* browsablesRootItem;
bool m_dropQualifiedNames;
bool m_dropEnquotedNames;
void buildTree(QTreeWidgetItem* parent, const std::string& schema);
QTreeWidgetItem* addNode(QTreeWidgetItem* parent, const sqlb::ObjectPtr& object, const std::string& schema);
QString getNameForDropping(const QString& domain, const QString& object, const QString& field) const;
};
#endif

View File

@ -0,0 +1,28 @@
#include "DotenvFormat.h"
#include <QRegularExpression>
#include <QTextStream>
bool DotenvFormat::readEnvFile(QIODevice &device, QSettings::SettingsMap &map)
{
QTextStream in(&device);
QString line;
QRegularExpression keyValueRegex("^\\s*([\\w\\.\\-]+)\\s*=\\s*(.*)\\s*$");
while (in.readLineInto(&line)) {
QRegularExpressionMatch match = keyValueRegex.match(line);
if (match.capturedLength() < 3) {
continue;
}
QString key = match.captured(1);
QString value = match.captured(2);
map.insert(key, value);
}
return true;
}

View File

@ -0,0 +1,14 @@
#ifndef DOTENVFORMAT_H
#define DOTENVFORMAT_H
#include <QSettings>
class QIODevice;
class DotenvFormat
{
public:
static bool readEnvFile(QIODevice &device, QSettings::SettingsMap &map);
};
#endif // DOTENVFORMAT_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,106 @@
#ifndef EDITDIALOG_H
#define EDITDIALOG_H
#include "WBFZExchangePluginAPI.h"
#include <QDialog>
#include <QPersistentModelIndex>
class QHexEdit;
class DockTextEdit;
namespace Ui {
class EditDialog;
}
class EditDialog : public QDialog
{
Q_OBJECT
public:
explicit EditDialog(QWidget* parent = nullptr);
~EditDialog() override;
void setCurrentIndex(const QModelIndex& idx);
QPersistentModelIndex currentIndex() { return m_currentIndex; };
public slots:
void setFocus();
void reject() override;
void setReadOnly(bool ro);
void reloadSettings();
protected:
void showEvent(QShowEvent* ev) override;
private slots:
void importData(bool asLink = false);
void exportData();
void setNull();
void updateApplyButton();
void accept() override;
void loadData(const QByteArray& bArrdata);
void toggleOverwriteMode();
void editModeChanged(int newMode);
void editTextChanged();
void switchEditorMode(bool autoSwitchForType);
void updateCellInfoAndMode(const QByteArray& bArrdata);
void setMustIndentAndCompact(bool enable);
void openPrintDialog();
void openPrintImageDialog();
void copyHexAscii();
void setWordWrapping(bool value);
signals:
void recordTextUpdated(const QPersistentModelIndex& idx, const QByteArray& bArrdata, bool isBlob);
void requestUrlOrFileOpen(const QString& urlString);
private:
Ui::EditDialog* ui;
QHexEdit* hexEdit;
DockTextEdit* sciEdit;
QPersistentModelIndex m_currentIndex;
int dataSource;
int dataType;
bool isReadOnly;
bool mustIndentAndCompact;
QByteArray removedBom;
enum DataSources {
QtBuffer,
HexBuffer,
SciBuffer
};
// SVG is both an Image and an XML document so it is treated separately
enum DataTypes {
Binary,
Image,
Null,
Text,
JSON,
SVG,
XML,
RtlText
};
// Edit modes and editor stack (this must be aligned with the UI).
// Note that text modes (plain, JSON and XML) share the Scintilla widget,
// Consequently the editor stack range is TextEditor..ImageViewer.
enum EditModes {
// Modes with their own widget in the stack:
TextEditor = 0,
RtlTextEditor = 1,
HexEditor = 2,
ImageViewer = 3,
// Modes in the Scintilla editor:
JsonEditor = 4,
XmlEditor = 5
};
int checkDataType(const QByteArray& bArrdata) const;
bool promptInvalidData(const QString& data_type, const QString& errorString);
void setDataInBuffer(const QByteArray& bArrdata, DataSources source);
void setStackCurrentIndex(int editMode);
void openDataWithExternal();
};
#endif

View File

@ -0,0 +1,690 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EditDialog</class>
<widget class="QDialog" name="EditDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>618</width>
<height>382</height>
</rect>
</property>
<property name="windowTitle">
<string>Edit database cell</string>
</property>
<property name="whatsThis">
<string>This area displays information about the data present in this database cell</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="labelMode">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Mode:</string>
</property>
<property name="buddy">
<cstring>comboMode</cstring>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboMode">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="whatsThis">
<string>This is the list of supported modes for the cell editor. Choose a mode for viewing or editing the data of the current cell.</string>
</property>
<item>
<property name="text">
<string>Text</string>
</property>
</item>
<item>
<property name="text">
<string>RTL Text</string>
</property>
</item>
<item>
<property name="text">
<string>Binary</string>
</property>
</item>
<item>
<property name="text">
<string>Image</string>
</property>
</item>
<item>
<property name="text">
<string>JSON</string>
</property>
</item>
<item>
<property name="text">
<string>XML</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QToolButton" name="buttonAutoSwitchMode">
<property name="toolTip">
<string>Automatically adjust the editor mode to the loaded data type</string>
</property>
<property name="statusTip">
<string>Automatically adjust the editor mode to the loaded data type</string>
</property>
<property name="whatsThis">
<string>This checkable button enables or disables the automatic switching of the editor mode. When a new cell is selected or new data is imported and the automatic switching is enabled, the mode adjusts to the detected data type. You can then change the editor mode manually. If you want to keep this manually switched mode while moving through the cells, switch the button off.</string>
</property>
<property name="text">
<string>Auto-switch</string>
</property>
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/keyword</normaloff>
<normalon>:/icons/cog_go.png</normalon>:/icons/keyword</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolBar" name="editCellToolbar">
<addaction name="actionWordWrap"/>
<addaction name="actionIndent"/>
<addaction name="actionImport"/>
<addaction name="actionExport"/>
<addaction name="actionOpenInExternal"/>
<addaction name="actionOpenInApp"/>
<addaction name="actionNull"/>
<addaction name="actionPrint"/>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QStackedWidget" name="editorStack">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="editorSci">
<property name="whatsThis">
<string>The text editor modes let you edit plain text, as well as JSON or XML data with syntax highlighting, automatic formatting and validation before saving.
Errors are indicated with a red squiggle underline.</string>
</property>
</widget>
<widget class="QWidget" name="rtlVerticalLayout">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QTextEdit" name="qtEdit">
<property name="font">
<font>
<family>Monospace</family>
</font>
</property>
<property name="whatsThis">
<string>This Qt editor is used for right-to-left scripts, which are not supported by the default Text editor. The presence of right-to-left characters is detected and this editor mode is automatically selected.</string>
</property>
<property name="acceptRichText">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="editorBinary">
<property name="contextMenuPolicy">
<enum>Qt::ActionsContextMenu</enum>
</property>
</widget>
<widget class="QScrollArea" name="editorImageScrollArea">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>84</width>
<height>35</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="editorImage">
<property name="contextMenuPolicy">
<enum>Qt::ActionsContextMenu</enum>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="labelType">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Type of data currently in cell</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelSize">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Size of data currently in table</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="buttonApply">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Apply data to cell</string>
</property>
<property name="whatsThis">
<string>This button saves the changes performed in the cell editor to the database cell.</string>
</property>
<property name="text">
<string>Apply</string>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
<action name="actionPrintImage">
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/print</normaloff>:/icons/print</iconset>
</property>
<property name="text">
<string>Print...</string>
</property>
<property name="toolTip">
<string>Open preview dialog for printing displayed image</string>
</property>
<property name="shortcut">
<string>Ctrl+P</string>
</property>
</action>
<action name="actionPrint">
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/print</normaloff>:/icons/print</iconset>
</property>
<property name="text">
<string>Print...</string>
</property>
<property name="toolTip">
<string>Open preview dialog for printing displayed text</string>
</property>
<property name="whatsThis">
<string>Open preview dialog for printing the data currently stored in the cell</string>
</property>
<property name="shortcut">
<string>Ctrl+P</string>
</property>
<property name="shortcutContext">
<enum>Qt::WidgetShortcut</enum>
</property>
</action>
<action name="actionCopyHexAscii">
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/special_copy</normaloff>:/icons/special_copy</iconset>
</property>
<property name="text">
<string>Copy Hex and ASCII</string>
</property>
<property name="toolTip">
<string>Copy selected hexadecimal and ASCII columns to the clipboard</string>
</property>
<property name="shortcut">
<string>Ctrl+Shift+C</string>
</property>
</action>
<action name="actionIndent">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/text_indent</normaloff>:/icons/text_indent</iconset>
</property>
<property name="text">
<string>Autoformat</string>
</property>
<property name="toolTip">
<string>Auto-format: pretty print on loading, compact on saving.</string>
</property>
<property name="whatsThis">
<string>When enabled, the auto-format feature formats the data on loading, breaking the text in lines and indenting it for maximum readability. On data saving, the auto-format feature compacts the data removing end of lines, and unnecessary whitespace.</string>
</property>
</action>
<action name="actionExport">
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/save_sql</normaloff>:/icons/save_sql</iconset>
</property>
<property name="text">
<string>&amp;Export...</string>
</property>
<property name="toolTip">
<string>Export to file</string>
</property>
<property name="whatsThis">
<string>Opens a file dialog used to export the contents of this database cell to a file.</string>
</property>
</action>
<action name="actionImport">
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/document_open</normaloff>:/icons/document_open</iconset>
</property>
<property name="text">
<string>&amp;Import...</string>
</property>
<property name="toolTip">
<string>Import from file</string>
</property>
<property name="whatsThis">
<string>Opens a file dialog used to import any kind of data to this database cell.</string>
</property>
</action>
<action name="actionNull">
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/set_to_null</normaloff>:/icons/set_to_null</iconset>
</property>
<property name="text">
<string>Set as &amp;NULL</string>
</property>
<property name="whatsThis">
<string>Erases the contents of the cell</string>
</property>
</action>
<action name="actionWordWrap">
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/word_wrap</normaloff>:/icons/word_wrap</iconset>
</property>
<property name="text">
<string>Word Wrap</string>
</property>
<property name="toolTip">
<string>Wrap lines on word boundaries</string>
</property>
</action>
<action name="actionOpenInApp">
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/open_in_app</normaloff>:/icons/open_in_app</iconset>
</property>
<property name="text">
<string>Open in default application or browser</string>
</property>
<property name="iconText">
<string>Open in application</string>
</property>
<property name="toolTip">
<string>Open in default application or browser</string>
</property>
<property name="whatsThis">
<string>The value is interpreted as a file or URL and opened in the default application or web browser.</string>
</property>
</action>
<action name="actionImportAsLink">
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/document_link</normaloff>:/icons/document_link</iconset>
</property>
<property name="text">
<string>Save file reference...</string>
</property>
<property name="toolTip">
<string>Save reference to file</string>
</property>
</action>
<action name="actionImportInMenu">
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/document_open</normaloff>:/icons/document_open</iconset>
</property>
<property name="text">
<string>&amp;Import...</string>
</property>
<property name="toolTip">
<string>Import from file</string>
</property>
<property name="whatsThis">
<string>Opens a file dialog used to import any kind of data to this database cell.</string>
</property>
</action>
<action name="actionOpenInExternal">
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/open_data_in_app</normaloff>:/icons/open_data_in_app</iconset>
</property>
<property name="text">
<string>Open in external application</string>
</property>
<property name="toolTip">
<string>Open in external application</string>
</property>
</action>
</widget>
<tabstops>
<tabstop>comboMode</tabstop>
<tabstop>buttonAutoSwitchMode</tabstop>
<tabstop>buttonApply</tabstop>
</tabstops>
<resources>
<include location="icons/icons.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonApply</sender>
<signal>clicked()</signal>
<receiver>EditDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>605</x>
<y>358</y>
</hint>
<hint type="destinationlabel">
<x>241</x>
<y>406</y>
</hint>
</hints>
</connection>
<connection>
<sender>comboMode</sender>
<signal>currentIndexChanged(int)</signal>
<receiver>EditDialog</receiver>
<slot>editModeChanged(int)</slot>
<hints>
<hint type="sourcelabel">
<x>71</x>
<y>27</y>
</hint>
<hint type="destinationlabel">
<x>201</x>
<y>431</y>
</hint>
</hints>
</connection>
<connection>
<sender>qtEdit</sender>
<signal>textChanged()</signal>
<receiver>EditDialog</receiver>
<slot>editTextChanged()</slot>
<hints>
<hint type="sourcelabel">
<x>279</x>
<y>191</y>
</hint>
<hint type="destinationlabel">
<x>339</x>
<y>335</y>
</hint>
</hints>
</connection>
<connection>
<sender>actionIndent</sender>
<signal>toggled(bool)</signal>
<receiver>EditDialog</receiver>
<slot>setMustIndentAndCompact(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>308</x>
<y>190</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonAutoSwitchMode</sender>
<signal>toggled(bool)</signal>
<receiver>EditDialog</receiver>
<slot>switchEditorMode(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>162</x>
<y>33</y>
</hint>
<hint type="destinationlabel">
<x>308</x>
<y>190</y>
</hint>
</hints>
</connection>
<connection>
<sender>actionPrintImage</sender>
<signal>triggered()</signal>
<receiver>EditDialog</receiver>
<slot>openPrintImageDialog()</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>308</x>
<y>190</y>
</hint>
</hints>
</connection>
<connection>
<sender>actionPrint</sender>
<signal>triggered()</signal>
<receiver>EditDialog</receiver>
<slot>openPrintDialog()</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>308</x>
<y>190</y>
</hint>
</hints>
</connection>
<connection>
<sender>actionCopyHexAscii</sender>
<signal>triggered()</signal>
<receiver>EditDialog</receiver>
<slot>copyHexAscii()</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>308</x>
<y>190</y>
</hint>
</hints>
</connection>
<connection>
<sender>actionNull</sender>
<signal>triggered()</signal>
<receiver>EditDialog</receiver>
<slot>setNull()</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>308</x>
<y>190</y>
</hint>
</hints>
</connection>
<connection>
<sender>actionImport</sender>
<signal>triggered()</signal>
<receiver>EditDialog</receiver>
<slot>importData()</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>308</x>
<y>190</y>
</hint>
</hints>
</connection>
<connection>
<sender>actionExport</sender>
<signal>triggered()</signal>
<receiver>EditDialog</receiver>
<slot>exportData()</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>308</x>
<y>190</y>
</hint>
</hints>
</connection>
<connection>
<sender>actionWordWrap</sender>
<signal>toggled(bool)</signal>
<receiver>EditDialog</receiver>
<slot>setWordWrapping(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>308</x>
<y>190</y>
</hint>
</hints>
</connection>
<connection>
<sender>actionImportInMenu</sender>
<signal>triggered()</signal>
<receiver>EditDialog</receiver>
<slot>importData()</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>308</x>
<y>190</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>importData()</slot>
<slot>exportData()</slot>
<slot>editTextChanged()</slot>
<slot>editModeChanged(int)</slot>
<slot>setNull()</slot>
</slots>
</ui>

View File

@ -0,0 +1,351 @@
#include "EditIndexDialog.h"
#include "ui_EditIndexDialog.h"
#include "sqlitedb.h"
#include "IconCache.h"
#include <QMessageBox>
#include <QPushButton>
EditIndexDialog::EditIndexDialog(DBBrowserDB& db, const sqlb::ObjectIdentifier& indexName, bool createIndex, QWidget* parent)
: QDialog(parent),
pdb(db),
curIndex(indexName),
index(indexName.name()),
newIndex(createIndex),
ui(new Ui::EditIndexDialog),
m_sRestorePointName(pdb.generateSavepointName("editindex"))
{
// Create UI
ui->setupUi(this);
ui->sqlTextEdit->setReadOnly(true);
// Get list of tables, sort it alphabetically and fill the combobox
std::map<std::string, sqlb::ObjectIdentifier> dbobjs; // Map from display name to full object identifier
if(newIndex) // If this is a new index, offer all tables of all database schemata
{
for(const auto& it : pdb.schemata)
{
auto tables = it.second.equal_range("table");
for(auto jt=tables.first;jt!=tables.second;++jt)
{
// Only show the schema name for non-main schemata
sqlb::ObjectIdentifier obj(it.first, jt->second->name());
dbobjs.insert({obj.toDisplayString(), obj});
}
}
} else { // If this is an existing index, only offer tables of the current database schema
auto tables = pdb.schemata[curIndex.schema()].equal_range("table");
for(auto it=tables.first;it!=tables.second;++it)
{
// Only show the schema name for non-main schemata
sqlb::ObjectIdentifier obj(curIndex.schema(), it->second->name());
dbobjs.insert({obj.toDisplayString(), obj});
}
}
ui->comboTableName->blockSignals(true);
for(auto it=dbobjs.cbegin();it!=dbobjs.cend();++it)
ui->comboTableName->addItem(IconCache::get("table"), QString::fromStdString(it->first), QString::fromStdString(it->second.toSerialised()));
ui->comboTableName->blockSignals(false);
QHeaderView *tableHeaderView = ui->tableIndexColumns->horizontalHeader();
tableHeaderView->setSectionResizeMode(0, QHeaderView::Stretch);
// Editing an existing index?
if(!newIndex)
{
// Load the current layout and fill in the dialog fields
index = *(pdb.getObjectByName<sqlb::Index>(curIndex));
ui->editIndexName->blockSignals(true);
ui->editIndexName->setText(QString::fromStdString(index.name()));
ui->editIndexName->blockSignals(false);
ui->checkIndexUnique->blockSignals(true);
ui->checkIndexUnique->setChecked(index.unique());
ui->checkIndexUnique->blockSignals(false);
ui->comboTableName->blockSignals(true);
ui->comboTableName->setCurrentText(QString::fromStdString(index.table()));
ui->comboTableName->blockSignals(false);
ui->editPartialClause->blockSignals(true);
ui->editPartialClause->setText(QString::fromStdString(index.whereExpr()));
ui->editPartialClause->blockSignals(false);
tableChanged(QString::fromStdString(index.table()), true);
} else {
tableChanged(ui->comboTableName->currentText(), false);
}
// Add event handler for index column name changes. These are only allowed for expression columns, though.
connect(ui->tableIndexColumns, &QTableWidget::itemChanged,
[=](QTableWidgetItem* item)
{
index.fields[static_cast<size_t>(item->row())].setName(item->text().toStdString());
updateSqlText();
});
// Create a savepoint to revert back to
pdb.setSavepoint(m_sRestorePointName);
}
EditIndexDialog::~EditIndexDialog()
{
delete ui;
}
void EditIndexDialog::tableChanged(const QString& new_table, bool initialLoad)
{
// Set the table name and clear all index columns
if(!initialLoad)
{
index.setTable(sqlb::ObjectIdentifier(ui->comboTableName->currentData().toString().toStdString()).name());
index.fields.clear();
}
// Stop here if table name is empty
if(new_table.isEmpty())
{
// Call checkInput() before to make sure the OK button is disabled
checkInput();
return;
}
updateColumnLists();
}
void EditIndexDialog::updateColumnLists()
{
// Fill the table column list
sqlb::TablePtr table = pdb.getObjectByName<sqlb::Table>(sqlb::ObjectIdentifier(ui->comboTableName->currentData().toString().toStdString()));
if(!table)
return;
sqlb::FieldInfoList tableFields = table->fieldInformation();
ui->tableTableColumns->setRowCount(static_cast<int>(tableFields.size()));
int tableRows = 0;
for(size_t i=0;i<tableFields.size();++i)
{
// When we're doing the initial loading and this field already is in the index to edit, then don't add it to the
// list of table columns. It will be added to the list of index columns in the next step. When this is not the initial
// loading, the index column list is empty, so this check will always be true.
if(sqlb::findField(index, tableFields.at(i).name) == index.fields.end())
{
// Put the name of the field in the first column
QTableWidgetItem* name = new QTableWidgetItem(QString::fromStdString(tableFields.at(i).name));
name->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
ui->tableTableColumns->setItem(tableRows, 0, name);
// Put the data type in the second column
QTableWidgetItem* type = new QTableWidgetItem(QString::fromStdString(tableFields.at(i).type));
type->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
ui->tableTableColumns->setItem(tableRows, 1, type);
tableRows++;
}
}
// Set row count to actual count. This is needed for the intial loading, when some rows might have been omitted because they were used in the index
ui->tableTableColumns->setRowCount(tableRows);
// Fill the index column list. This is done separately from the table column to include expression columns (these are not found in the original
// table) and to preserve the order of the index columns
auto indexFields = index.fields;
ui->tableIndexColumns->blockSignals(true);
ui->tableIndexColumns->setRowCount(static_cast<int>(indexFields.size()));
for(size_t i=0;i<indexFields.size();++i)
{
// Put the name of the field in the first column
QTableWidgetItem* name = new QTableWidgetItem(QString::fromStdString(indexFields.at(i).name()));
Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
if(indexFields.at(i).expression())
flags |= Qt::ItemIsEditable;
name->setFlags(flags);
ui->tableIndexColumns->setItem(static_cast<int>(i), 0, name);
// And put a combobox to select the order in which to index the field in the last column
QComboBox* order = new QComboBox(this);
order->addItem("");
order->addItem("ASC");
order->addItem("DESC");
order->setCurrentText(QString::fromStdString(indexFields.at(i).order()).toUpper());
ui->tableIndexColumns->setCellWidget(static_cast<int>(i), 1, order);
connect(order, &QComboBox::currentTextChanged,
[=](QString new_order)
{
auto colnum = sqlb::findField(index, indexFields.at(i).name());
if(colnum != index.fields.end())
{
colnum->setOrder(new_order.toStdString());
updateSqlText();
}
});
}
ui->tableIndexColumns->blockSignals(false);
checkInput();
}
void EditIndexDialog::addToIndex(const QModelIndex& idx)
{
// Get current row number
int row;
if(idx.isValid())
row = idx.row();
else
row = ui->tableTableColumns->currentRow();
// No row selected? Abort.
if(row == -1)
return;
// Add field to index
index.fields.emplace_back(
ui->tableTableColumns->item(row, 0)->text().toStdString(), // Column name
false, // Is expression
""); // Order
// Update UI
updateColumnLists();
}
void EditIndexDialog::removeFromIndex(const QModelIndex& idx)
{
// Get current row number
int row;
if(idx.isValid())
row = idx.row();
else
row = ui->tableIndexColumns->currentRow();
// No row selected? Abort.
if(row == -1)
return;
// If this is an expression column and the action was triggered by a double click event instead of a button click,
// we won't remove the expression column because it's too likely that this was only done by accident by the user.
// Instead just open the expression column for editing.
if(index.fields[static_cast<size_t>(row)].expression() && sender() != ui->buttonFromIndex)
{
ui->tableIndexColumns->editItem(ui->tableIndexColumns->item(row, 0));
return;
}
// Remove column from index
sqlb::removeField(index, ui->tableIndexColumns->item(row, 0)->text().toStdString());
// Update UI
updateColumnLists();
}
void EditIndexDialog::checkInput()
{
// Check if index name is set
bool valid = true;
if(ui->editIndexName->text().isEmpty())
valid = false;
// Check if a table is selected (this is especially important in the case where there are no tables in the database yet).
if(ui->comboTableName->currentText().isNull())
valid = false;
// Check if index has any columns
if(index.fields.size() == 0)
valid = false;
// Only activate OK button if index data is valid
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(valid);
// Set the index name and the unique flag
index.setName(ui->editIndexName->text().toStdString());
index.setUnique(ui->checkIndexUnique->isChecked());
index.setWhereExpr(ui->editPartialClause->text().toStdString());
updateSqlText();
}
void EditIndexDialog::accept()
{
// When editing an index, delete the old one first
if(!newIndex)
{
if(!pdb.executeSQL("DROP INDEX IF EXISTS " + curIndex.toString()))
{
QMessageBox::warning(this, qApp->applicationName(), tr("Deleting the old index failed:\n%1").arg(pdb.lastError()));
return;
}
}
// Create the new index in the schema of the selected table
if(pdb.executeSQL(index.sql(sqlb::ObjectIdentifier(ui->comboTableName->currentData().toString().toStdString()).schema())))
QDialog::accept();
else
QMessageBox::warning(this, QApplication::applicationName(), tr("Creating the index failed:\n%1").arg(pdb.lastError()));
}
void EditIndexDialog::reject()
{
// Rollback to our savepoint
pdb.revertToSavepoint(m_sRestorePointName);
QDialog::reject();
}
void EditIndexDialog::updateSqlText()
{
ui->sqlTextEdit->setText(QString::fromStdString(index.sql(sqlb::ObjectIdentifier(ui->comboTableName->currentData().toString().toStdString()).schema())));
}
void EditIndexDialog::moveColumnUp()
{
moveCurrentColumn(false);
}
void EditIndexDialog::moveColumnDown()
{
moveCurrentColumn(true);
}
void EditIndexDialog::moveCurrentColumn(bool down)
{
// Get current row number and calculate row number after the movement. Check the values
int currentRow = ui->tableIndexColumns->currentRow();
if(currentRow == -1)
return;
int newRow = currentRow + (down ? 1 : -1);
if(newRow < 0)
return;
if(newRow >= ui->tableIndexColumns->rowCount())
return;
// Swap the columns
std::swap(index.fields[static_cast<size_t>(currentRow)], index.fields[static_cast<size_t>(newRow)]);
// Update UI
updateColumnLists();
// Select old row at new position
ui->tableIndexColumns->selectRow(newRow);
}
void EditIndexDialog::addExpressionColumn()
{
// Check if there already is an empty expression column
auto field_it = sqlb::findField(index, "");
int row = static_cast<int>(std::distance(index.fields.begin(), field_it));
if(field_it == index.fields.end())
{
// There is no empty expression column yet, so add one.
// Add new expression column to the index
index.fields.emplace_back(
"", // Column name
true, // Is expression
""); // Order
// Update UI
updateColumnLists();
// Get row number of new column
row = ui->tableIndexColumns->rowCount() - 1;
}
// Now we should have the row number of the empty expression column, no matter if it was newly added or it already existed.
// Select the row for editing
ui->tableIndexColumns->editItem(ui->tableIndexColumns->item(row, 0));
}

View File

@ -0,0 +1,49 @@
#ifndef EDITINDEXDIALOG_H
#define EDITINDEXDIALOG_H
#include "WBFZExchangePluginAPI.h"
#include "sql/ObjectIdentifier.h"
#include "sql/sqlitetypes.h"
#include <QDialog>
#include <QModelIndex>
class DBBrowserDB;
namespace Ui {
class EditIndexDialog;
}
class EditIndexDialog : public QDialog
{
Q_OBJECT
public:
explicit EditIndexDialog(DBBrowserDB& db, const sqlb::ObjectIdentifier& indexName, bool createIndex, QWidget* parent = nullptr);
~EditIndexDialog() override;
private slots:
void accept() override;
void reject() override;
void tableChanged(const QString& new_table, bool initialLoad = false);
void checkInput();
void addToIndex(const QModelIndex& idx = QModelIndex());
void removeFromIndex(const QModelIndex& idx = QModelIndex());
void moveColumnUp();
void moveColumnDown();
void addExpressionColumn();
private:
DBBrowserDB& pdb;
sqlb::ObjectIdentifier curIndex;
sqlb::Index index;
bool newIndex;
Ui::EditIndexDialog* ui;
std::string m_sRestorePointName;
void updateColumnLists();
void updateSqlText();
void moveCurrentColumn(bool down);
};
#endif

View File

@ -0,0 +1,573 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EditIndexDialog</class>
<widget class="QDialog" name="EditIndexDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>703</width>
<height>543</height>
</rect>
</property>
<property name="windowTitle">
<string>Edit Index Schema</string>
</property>
<property name="windowIcon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/index_create</normaloff>:/icons/index_create</iconset>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="labelIndexName">
<property name="text">
<string>&amp;Name</string>
</property>
<property name="buddy">
<cstring>editIndexName</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="editIndexName"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelTableName">
<property name="text">
<string>&amp;Table</string>
</property>
<property name="buddy">
<cstring>comboTableName</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="comboTableName"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="labelIndexUnique">
<property name="text">
<string>&amp;Unique</string>
</property>
<property name="buddy">
<cstring>checkIndexUnique</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="checkIndexUnique">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label">
<property name="toolTip">
<string>For restricting the index to only a part of the table you can specify a WHERE clause here that selects the part of the table that should be indexed</string>
</property>
<property name="text">
<string>Partial inde&amp;x clause</string>
</property>
<property name="buddy">
<cstring>editPartialClause</cstring>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="editPartialClause"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="labelIndexColumns">
<property name="text">
<string>Colu&amp;mns</string>
</property>
<property name="buddy">
<cstring>tableIndexColumns</cstring>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSplitter" name="splitter">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QWidget" name="layoutWidget">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QTableWidget" name="tableTableColumns">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string>Table column</string>
</property>
</column>
<column>
<property name="text">
<string>Type</string>
</property>
</column>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="buttonToIndex">
<property name="arrowType">
<enum>Qt::RightArrow</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="buttonFromIndex">
<property name="arrowType">
<enum>Qt::LeftArrow</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="buttonAddExpressionColumn">
<property name="toolTip">
<string>Add a new expression column to the index. Expression columns contain SQL expression rather than column names.</string>
</property>
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/cog_go.png</normaloff>:/icons/cog_go.png</iconset>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QTableWidget" name="tableIndexColumns">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>250</height>
</size>
</property>
<property name="editTriggers">
<set>QAbstractItemView::AnyKeyPressed|QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked</set>
</property>
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string>Index column</string>
</property>
</column>
<column>
<property name="text">
<string>Order</string>
</property>
</column>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="buttonMoveColumnUp">
<property name="arrowType">
<enum>Qt::UpArrow</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="buttonMoveColumnDown">
<property name="arrowType">
<enum>Qt::DownArrow</enum>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="SqlTextEdit" name="sqlTextEdit" native="true">
<property name="minimumSize">
<size>
<width>0</width>
<height>100</height>
</size>
</property>
<property name="readOnly" stdset="0">
<bool>true</bool>
</property>
</widget>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>SqlTextEdit</class>
<extends>QWidget</extends>
<header>sqltextedit.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>editIndexName</tabstop>
<tabstop>comboTableName</tabstop>
<tabstop>checkIndexUnique</tabstop>
<tabstop>editPartialClause</tabstop>
<tabstop>tableTableColumns</tabstop>
<tabstop>tableIndexColumns</tabstop>
<tabstop>buttonToIndex</tabstop>
<tabstop>buttonFromIndex</tabstop>
</tabstops>
<resources>
<include location="icons/icons.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>EditIndexDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>264</x>
<y>536</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>EditIndexDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>332</x>
<y>536</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>comboTableName</sender>
<signal>currentIndexChanged(QString)</signal>
<receiver>EditIndexDialog</receiver>
<slot>tableChanged(QString)</slot>
<hints>
<hint type="sourcelabel">
<x>145</x>
<y>74</y>
</hint>
<hint type="destinationlabel">
<x>236</x>
<y>31</y>
</hint>
</hints>
</connection>
<connection>
<sender>editIndexName</sender>
<signal>textChanged(QString)</signal>
<receiver>EditIndexDialog</receiver>
<slot>checkInput()</slot>
<hints>
<hint type="sourcelabel">
<x>429</x>
<y>14</y>
</hint>
<hint type="destinationlabel">
<x>443</x>
<y>39</y>
</hint>
</hints>
</connection>
<connection>
<sender>tableIndexColumns</sender>
<signal>cellChanged(int,int)</signal>
<receiver>EditIndexDialog</receiver>
<slot>checkInput()</slot>
<hints>
<hint type="sourcelabel">
<x>443</x>
<y>170</y>
</hint>
<hint type="destinationlabel">
<x>566</x>
<y>40</y>
</hint>
</hints>
</connection>
<connection>
<sender>checkIndexUnique</sender>
<signal>toggled(bool)</signal>
<receiver>EditIndexDialog</receiver>
<slot>checkInput()</slot>
<hints>
<hint type="sourcelabel">
<x>153</x>
<y>100</y>
</hint>
<hint type="destinationlabel">
<x>304</x>
<y>251</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonToIndex</sender>
<signal>clicked()</signal>
<receiver>EditIndexDialog</receiver>
<slot>addToIndex()</slot>
<hints>
<hint type="sourcelabel">
<x>406</x>
<y>247</y>
</hint>
<hint type="destinationlabel">
<x>385</x>
<y>103</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonFromIndex</sender>
<signal>clicked()</signal>
<receiver>EditIndexDialog</receiver>
<slot>removeFromIndex()</slot>
<hints>
<hint type="sourcelabel">
<x>406</x>
<y>285</y>
</hint>
<hint type="destinationlabel">
<x>350</x>
<y>95</y>
</hint>
</hints>
</connection>
<connection>
<sender>tableIndexColumns</sender>
<signal>doubleClicked(QModelIndex)</signal>
<receiver>EditIndexDialog</receiver>
<slot>removeFromIndex(QModelIndex)</slot>
<hints>
<hint type="sourcelabel">
<x>520</x>
<y>236</y>
</hint>
<hint type="destinationlabel">
<x>684</x>
<y>175</y>
</hint>
</hints>
</connection>
<connection>
<sender>tableTableColumns</sender>
<signal>doubleClicked(QModelIndex)</signal>
<receiver>EditIndexDialog</receiver>
<slot>addToIndex(QModelIndex)</slot>
<hints>
<hint type="sourcelabel">
<x>231</x>
<y>357</y>
</hint>
<hint type="destinationlabel">
<x>19</x>
<y>205</y>
</hint>
</hints>
</connection>
<connection>
<sender>editPartialClause</sender>
<signal>textChanged(QString)</signal>
<receiver>EditIndexDialog</receiver>
<slot>checkInput()</slot>
<hints>
<hint type="sourcelabel">
<x>242</x>
<y>129</y>
</hint>
<hint type="destinationlabel">
<x>47</x>
<y>103</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonMoveColumnUp</sender>
<signal>clicked()</signal>
<receiver>EditIndexDialog</receiver>
<slot>moveColumnUp()</slot>
<hints>
<hint type="sourcelabel">
<x>676</x>
<y>241</y>
</hint>
<hint type="destinationlabel">
<x>700</x>
<y>212</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonMoveColumnDown</sender>
<signal>clicked()</signal>
<receiver>EditIndexDialog</receiver>
<slot>moveColumnDown()</slot>
<hints>
<hint type="sourcelabel">
<x>686</x>
<y>299</y>
</hint>
<hint type="destinationlabel">
<x>699</x>
<y>307</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonAddExpressionColumn</sender>
<signal>clicked()</signal>
<receiver>EditIndexDialog</receiver>
<slot>addExpressionColumn()</slot>
<hints>
<hint type="sourcelabel">
<x>398</x>
<y>311</y>
</hint>
<hint type="destinationlabel">
<x>379</x>
<y>500</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>tableChanged(QString)</slot>
<slot>checkInput()</slot>
<slot>addToIndex()</slot>
<slot>addToIndex(QModelIndex)</slot>
<slot>removeFromIndex()</slot>
<slot>removeFromIndex(QModelIndex)</slot>
<slot>moveColumnUp()</slot>
<slot>moveColumnDown()</slot>
<slot>addExpressionColumn()</slot>
</slots>
</ui>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,101 @@
#ifndef EDITTABLEDIALOG_H
#define EDITTABLEDIALOG_H
#include "WBFZExchangePluginAPI.h"
#include "sql/ObjectIdentifier.h"
#include "sql/sqlitetypes.h"
#include <map>
#include <QDialog>
class DBBrowserDB;
class ForeignKeyEditorDelegate;
class QTableWidgetItem;
class QTreeWidgetItem;
namespace Ui {
class EditTableDialog;
}
class EditTableDialog : public QDialog
{
Q_OBJECT
public:
explicit EditTableDialog(DBBrowserDB& pdb, const sqlb::ObjectIdentifier& tableName, bool createTable, QWidget* parent = nullptr);
~EditTableDialog() override;
protected:
void keyPressEvent(QKeyEvent *evt) override;
private:
enum Columns {
kName = 0,
kType = 1,
kNotNull = 2,
kPrimaryKey = 3,
kAutoIncrement = 4,
kUnique = 5,
kDefault = 6,
kCheck = 7,
kCollation = 8,
kForeignKey = 9
};
enum ConstraintColumns {
kConstraintColumns = 0,
kConstraintType = 1,
kConstraintName = 2,
kConstraintSql = 3
};
enum MoveFieldDirection
{
MoveUp,
MoveDown,
MoveTop,
MoveBottom
};
void updateColumnWidth();
void updateSqlText();
void moveCurrentField(MoveFieldDirection dir);
private slots:
void populateFields();
void populateConstraints();
void addField();
void removeField();
void fieldSelectionChanged();
void accept() override;
void reject() override;
void checkInput();
void fieldItemChanged(QTreeWidgetItem* item, int column);
void constraintItemChanged(QTableWidgetItem* item);
void updateTypeAndCollation(QObject *object);
bool eventFilter(QObject *object, QEvent *event) override;
void updateTypeAndCollation();
void moveUp();
void moveDown();
void moveTop();
void moveBottom();
void setWithoutRowid(bool without_rowid);
void changeSchema(const QString& schema);
void removeConstraint();
void addConstraint(sqlb::Constraint::ConstraintTypes type);
private:
Ui::EditTableDialog* ui;
DBBrowserDB& pdb;
ForeignKeyEditorDelegate* m_fkEditorDelegate;
sqlb::ObjectIdentifier curTable;
std::map<QString, QString> trackColumns;
sqlb::Table m_table;
bool m_bNewTable;
std::string m_sRestorePointName;
QStringList m_collationList;
};
#endif

View File

@ -0,0 +1,782 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EditTableDialog</class>
<widget class="QDialog" name="EditTableDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>652</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>Edit table definition</string>
</property>
<property name="windowIcon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/table</normaloff>:/icons/table</iconset>
</property>
<property name="sizeGripEnabled">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QGroupBox" name="groupTable">
<property name="title">
<string>Table</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLineEdit" name="editTableName">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="buttonMore">
<property name="text">
<string>Advanced</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
<property name="arrowType">
<enum>Qt::DownArrow</enum>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="widgetExtension" native="true">
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Database sche&amp;ma</string>
</property>
<property name="buddy">
<cstring>comboSchema</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboSchema"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_1">
<property name="text">
<string>Without Rowid</string>
</property>
<property name="buddy">
<cstring>checkWithoutRowid</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="checkWithoutRowid">
<property name="toolTip">
<string>Make this a 'WITHOUT rowid' table. Setting this flag requires a field of type INTEGER with the primary key flag set and the auto increment flag unset.</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QTabWidget" name="groupDefinition">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="groupFieldsPage1">
<attribute name="title">
<string>Fields</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QToolButton" name="addFieldButton">
<property name="text">
<string>Add</string>
</property>
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/field_add</normaloff>:/icons/field_add</iconset>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="removeFieldButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Remove</string>
</property>
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/field_delete</normaloff>:/icons/field_delete</iconset>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="buttonMoveTop">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Move to top</string>
</property>
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/arrow_top</normaloff>:/icons/arrow_top</iconset>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
<property name="arrowType">
<enum>Qt::NoArrow</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="buttonMoveUp">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Move up</string>
</property>
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/up</normaloff>:/icons/up</iconset>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="buttonMoveDown">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Move down</string>
</property>
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/down</normaloff>:/icons/down</iconset>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="buttonMoveBottom">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Move to bottom</string>
</property>
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/arrow_bottom</normaloff>:/icons/arrow_bottom</iconset>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QTreeWidget" name="treeWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>140</height>
</size>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<column>
<property name="text">
<string>Name</string>
</property>
</column>
<column>
<property name="text">
<string>Type</string>
</property>
</column>
<column>
<property name="text">
<string>NN</string>
</property>
<property name="toolTip">
<string>Not null</string>
</property>
</column>
<column>
<property name="text">
<string>PK</string>
</property>
<property name="toolTip">
<string>Primary key</string>
</property>
</column>
<column>
<property name="text">
<string>AI</string>
</property>
<property name="toolTip">
<string>Autoincrement</string>
</property>
</column>
<column>
<property name="text">
<string>U</string>
</property>
<property name="toolTip">
<string>Unique</string>
</property>
</column>
<column>
<property name="text">
<string>Default</string>
</property>
<property name="toolTip">
<string>Default value</string>
</property>
</column>
<column>
<property name="text">
<string>Check</string>
</property>
<property name="toolTip">
<string>Check constraint</string>
</property>
</column>
<column>
<property name="text">
<string>Collation</string>
</property>
</column>
<column>
<property name="text">
<string>Foreign Key</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Constraints</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QToolButton" name="buttonAddConstraint">
<property name="text">
<string>Add constraint</string>
</property>
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/field_add</normaloff>:/icons/field_add</iconset>
</property>
<property name="popupMode">
<enum>QToolButton::InstantPopup</enum>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="buttonRemoveConstraint">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Remove constraint</string>
</property>
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/field_delete</normaloff>:/icons/field_delete</iconset>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QTableWidget" name="tableConstraints">
<property name="horizontalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<column>
<property name="text">
<string>Columns</string>
</property>
</column>
<column>
<property name="text">
<string>Type</string>
</property>
</column>
<column>
<property name="text">
<string>Name</string>
</property>
</column>
<column>
<property name="text">
<string>SQL</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
</widget>
<widget class="SqlTextEdit" name="sqlTextEdit" native="true">
<property name="readOnly" stdset="0">
<bool>true</bool>
</property>
</widget>
</widget>
</item>
<item>
<widget class="QLabel" name="labelEditWarning">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; color:#ff0000;&quot;&gt;Warning: &lt;/span&gt;There is something with this table definition that our parser doesn't fully understand. Modifying and saving this table might result in problems.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
<action name="actionAddPrimaryKey">
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/field_key</normaloff>:/icons/field_key</iconset>
</property>
<property name="text">
<string>Primary Key</string>
</property>
<property name="toolTip">
<string>Add a primary key constraint</string>
</property>
</action>
<action name="actionAddForeignKey">
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/field_fk</normaloff>:/icons/field_fk</iconset>
</property>
<property name="text">
<string>Foreign Key</string>
</property>
<property name="toolTip">
<string>Add a foreign key constraint</string>
</property>
</action>
<action name="actionAddUniqueConstraint">
<property name="text">
<string>Unique</string>
</property>
<property name="toolTip">
<string>Add a unique constraint</string>
</property>
</action>
<action name="actionAddCheckConstraint">
<property name="text">
<string>Check</string>
</property>
<property name="toolTip">
<string>Add a check constraint</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>SqlTextEdit</class>
<extends>QWidget</extends>
<header>sqltextedit.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>editTableName</tabstop>
<tabstop>buttonMore</tabstop>
<tabstop>comboSchema</tabstop>
<tabstop>checkWithoutRowid</tabstop>
<tabstop>groupDefinition</tabstop>
<tabstop>addFieldButton</tabstop>
<tabstop>removeFieldButton</tabstop>
<tabstop>buttonMoveTop</tabstop>
<tabstop>buttonMoveUp</tabstop>
<tabstop>buttonMoveDown</tabstop>
<tabstop>buttonMoveBottom</tabstop>
<tabstop>treeWidget</tabstop>
<tabstop>sqlTextEdit</tabstop>
<tabstop>buttonAddConstraint</tabstop>
<tabstop>buttonRemoveConstraint</tabstop>
<tabstop>tableConstraints</tabstop>
</tabstops>
<resources>
<include location="icons/icons.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>EditTableDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>261</x>
<y>590</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>EditTableDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>329</x>
<y>590</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>treeWidget</sender>
<signal>itemSelectionChanged()</signal>
<receiver>EditTableDialog</receiver>
<slot>fieldSelectionChanged()</slot>
<hints>
<hint type="sourcelabel">
<x>137</x>
<y>367</y>
</hint>
<hint type="destinationlabel">
<x>411</x>
<y>181</y>
</hint>
</hints>
</connection>
<connection>
<sender>addFieldButton</sender>
<signal>clicked()</signal>
<receiver>EditTableDialog</receiver>
<slot>addField()</slot>
<hints>
<hint type="sourcelabel">
<x>78</x>
<y>255</y>
</hint>
<hint type="destinationlabel">
<x>79</x>
<y>65</y>
</hint>
</hints>
</connection>
<connection>
<sender>removeFieldButton</sender>
<signal>clicked()</signal>
<receiver>EditTableDialog</receiver>
<slot>removeField()</slot>
<hints>
<hint type="sourcelabel">
<x>167</x>
<y>255</y>
</hint>
<hint type="destinationlabel">
<x>249</x>
<y>63</y>
</hint>
</hints>
</connection>
<connection>
<sender>editTableName</sender>
<signal>textChanged(QString)</signal>
<receiver>EditTableDialog</receiver>
<slot>checkInput()</slot>
<hints>
<hint type="sourcelabel">
<x>62</x>
<y>48</y>
</hint>
<hint type="destinationlabel">
<x>115</x>
<y>3</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonMoveUp</sender>
<signal>clicked()</signal>
<receiver>EditTableDialog</receiver>
<slot>moveUp()</slot>
<hints>
<hint type="sourcelabel">
<x>343</x>
<y>253</y>
</hint>
<hint type="destinationlabel">
<x>308</x>
<y>235</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonMoveDown</sender>
<signal>clicked()</signal>
<receiver>EditTableDialog</receiver>
<slot>moveDown()</slot>
<hints>
<hint type="sourcelabel">
<x>481</x>
<y>253</y>
</hint>
<hint type="destinationlabel">
<x>308</x>
<y>235</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonMore</sender>
<signal>toggled(bool)</signal>
<receiver>widgetExtension</receiver>
<slot>setVisible(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>97</x>
<y>78</y>
</hint>
<hint type="destinationlabel">
<x>138</x>
<y>172</y>
</hint>
</hints>
</connection>
<connection>
<sender>checkWithoutRowid</sender>
<signal>toggled(bool)</signal>
<receiver>EditTableDialog</receiver>
<slot>setWithoutRowid(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>485</x>
<y>160</y>
</hint>
<hint type="destinationlabel">
<x>324</x>
<y>299</y>
</hint>
</hints>
</connection>
<connection>
<sender>treeWidget</sender>
<signal>currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)</signal>
<receiver>EditTableDialog</receiver>
<slot>fieldSelectionChanged()</slot>
<hints>
<hint type="sourcelabel">
<x>324</x>
<y>294</y>
</hint>
<hint type="destinationlabel">
<x>324</x>
<y>299</y>
</hint>
</hints>
</connection>
<connection>
<sender>comboSchema</sender>
<signal>currentIndexChanged(QString)</signal>
<receiver>EditTableDialog</receiver>
<slot>changeSchema(QString)</slot>
<hints>
<hint type="sourcelabel">
<x>327</x>
<y>139</y>
</hint>
<hint type="destinationlabel">
<x>647</x>
<y>157</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonRemoveConstraint</sender>
<signal>clicked()</signal>
<receiver>EditTableDialog</receiver>
<slot>removeConstraint()</slot>
<hints>
<hint type="sourcelabel">
<x>295</x>
<y>255</y>
</hint>
<hint type="destinationlabel">
<x>647</x>
<y>157</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonMoveTop</sender>
<signal>clicked()</signal>
<receiver>EditTableDialog</receiver>
<slot>moveTop()</slot>
<hints>
<hint type="sourcelabel">
<x>207</x>
<y>240</y>
</hint>
<hint type="destinationlabel">
<x>202</x>
<y>190</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonMoveBottom</sender>
<signal>clicked()</signal>
<receiver>EditTableDialog</receiver>
<slot>moveBottom()</slot>
<hints>
<hint type="sourcelabel">
<x>530</x>
<y>246</y>
</hint>
<hint type="destinationlabel">
<x>400</x>
<y>186</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>fieldSelectionChanged()</slot>
<slot>addField()</slot>
<slot>editField()</slot>
<slot>removeField()</slot>
<slot>checkInput()</slot>
<slot>itemChanged()</slot>
<slot>moveUp()</slot>
<slot>moveDown()</slot>
<slot>setWithoutRowid(bool)</slot>
<slot>changeSchema(QString)</slot>
<slot>removeConstraint()</slot>
<slot>moveTop()</slot>
<slot>moveBottom()</slot>
</slots>
</ui>

View File

@ -0,0 +1,585 @@
#include "ExportDataDialog.h"
#include "ui_ExportDataDialog.h"
#include "sqlitedb.h"
#include "Settings.h"
#include "sqlite.h"
#include "FileDialog.h"
#include "IconCache.h"
#include <QFile>
#include <QTextStream>
#include <QMessageBox>
#include <QTextCodec>
#include <json.hpp>
using json = nlohmann::json;
ExportDataDialog::ExportDataDialog(DBBrowserDB& db, ExportFormats format, QWidget* parent, const std::string& query, const sqlb::ObjectIdentifier& selection)
: QDialog(parent),
ui(new Ui::ExportDataDialog),
pdb(db),
m_format(format),
m_sQuery(query)
{
// Create UI
ui->setupUi(this);
// Show different option widgets depending on the export format
ui->stackFormat->setCurrentIndex(format);
if(format == ExportFormatJson) {
setWindowTitle(tr("Export data as JSON"));
}
// Retrieve the saved dialog preferences
ui->checkHeader->setChecked(Settings::getValue("exportcsv", "firstrowheader").toBool());
setSeparatorChar(Settings::getValue("exportcsv", "separator").toInt());
setQuoteChar(Settings::getValue("exportcsv", "quotecharacter").toInt());
setNewLineString(Settings::getValue("exportcsv", "newlinecharacters").toString());
ui->checkPrettyPrint->setChecked(Settings::getValue("exportjson", "prettyprint").toBool());
// Update the visible/hidden status of the "Other" line edit fields
showCustomCharEdits();
// If a SQL query was specified hide the table combo box. If not fill it with tables to export
if(query.empty())
{
// Get list of tables to export
for(const auto& it : pdb.schemata)
{
const auto tables = it.second.equal_range("table");
const auto views = it.second.equal_range("view");
std::map<std::string, sqlb::ObjectPtr> objects;
for(auto jt=tables.first;jt!=tables.second;++jt)
objects.insert({jt->second->name(), jt->second});
for(auto jt=views.first;jt!=views.second;++jt)
objects.insert({jt->second->name(), jt->second});
for(const auto& jt : objects)
{
sqlb::ObjectIdentifier obj(it.first, jt.second->name());
QListWidgetItem* item = new QListWidgetItem(IconCache::get(sqlb::Object::typeToString(jt.second->type())), QString::fromStdString(obj.toDisplayString()));
item->setData(Qt::UserRole, QString::fromStdString(obj.toSerialised()));
ui->listTables->addItem(item);
}
}
// Sort list of tables and select the table specified in the selection parameter or alternatively the first one
ui->listTables->model()->sort(0);
if(selection.isEmpty())
{
ui->listTables->setCurrentItem(ui->listTables->item(0));
} else {
for(int i=0;i<ui->listTables->count();i++)
{
if(sqlb::ObjectIdentifier(ui->listTables->item(i)->data(Qt::UserRole).toString().toStdString()) == selection)
{
ui->listTables->setCurrentRow(i);
break;
}
}
}
} else {
// Hide table combo box
ui->labelTable->setVisible(false);
ui->listTables->setVisible(false);
resize(minimumSize());
}
}
ExportDataDialog::~ExportDataDialog()
{
delete ui;
}
bool ExportDataDialog::exportQuery(const std::string& sQuery, const QString& sFilename)
{
switch(m_format)
{
case ExportFormatCsv:
return exportQueryCsv(sQuery, sFilename);
case ExportFormatJson:
return exportQueryJson(sQuery, sFilename);
}
return false;
}
bool ExportDataDialog::exportQueryCsv(const std::string& sQuery, const QString& sFilename)
{
// Prepare the quote and separating characters
QChar quoteChar = currentQuoteChar();
QString quotequoteChar = QString(quoteChar) + quoteChar;
QChar sepChar = currentSeparatorChar();
QString newlineStr = currentNewLineString();
// Chars that require escaping
std::string special_chars = newlineStr.toStdString() + sepChar.toLatin1() + quoteChar.toLatin1();
// Open file
QFile file(sFilename);
if(file.open(QIODevice::WriteOnly))
{
// Open text stream to the file
QTextStream stream(&file);
auto pDb = pdb.get(tr("exporting CSV"));
sqlite3_stmt* stmt;
int status = sqlite3_prepare_v2(pDb.get(), sQuery.c_str(), static_cast<int>(sQuery.size()), &stmt, nullptr);
if(SQLITE_OK == status)
{
if(ui->checkHeader->isChecked())
{
int columns = sqlite3_column_count(stmt);
for (int i = 0; i < columns; ++i)
{
QString content = QString::fromUtf8(sqlite3_column_name(stmt, i));
if(content.toStdString().find_first_of(special_chars) != std::string::npos)
stream << quoteChar << content.replace(quoteChar, quotequoteChar) << quoteChar;
else
stream << content;
if(i != columns - 1)
// Only output the separator value if sepChar isn't 0,
// as that's used to indicate no separator character
// should be used
if(!sepChar.isNull())
stream << sepChar;
}
stream << newlineStr;
}
QApplication::setOverrideCursor(Qt::WaitCursor);
int columns = sqlite3_column_count(stmt);
size_t counter = 0;
while(sqlite3_step(stmt) == SQLITE_ROW)
{
for (int i = 0; i < columns; ++i)
{
QString content = QString::fromUtf8(
reinterpret_cast<const char*>(sqlite3_column_blob(stmt, i)),
sqlite3_column_bytes(stmt, i));
// If no quote char is set but the content contains a line break, we enforce some quote characters. This probably isn't entirely correct
// but still better than having the line breaks unquoted and effectively outputting a garbage file.
if(quoteChar.isNull() && content.contains(newlineStr))
stream << '"' << content.replace('"', "\"\"") << '"';
// If the content needs to be quoted, quote it. But only if a quote char has been specified
else if(!quoteChar.isNull() && content.toStdString().find_first_of(special_chars) != std::string::npos)
stream << quoteChar << content.replace(quoteChar, quotequoteChar) << quoteChar;
// If it doesn't need to be quoted, don't quote it
else
stream << content;
if(i != columns - 1)
// Only output the separator value if sepChar isn't 0,
// as that's used to indicate no separator character
// should be used
if(!sepChar.isNull())
stream << sepChar;
}
stream << newlineStr;
if(counter % 1000 == 0)
qApp->processEvents();
counter++;
}
}
sqlite3_finalize(stmt);
QApplication::restoreOverrideCursor();
qApp->processEvents();
// Done writing the file
file.close();
} else {
QMessageBox::warning(this, QApplication::applicationName(),
tr("Could not open output file: %1").arg(sFilename));
return false;
}
return true;
}
bool ExportDataDialog::exportQueryJson(const std::string& sQuery, const QString& sFilename)
{
// Open file
QFile file(sFilename);
if(file.open(QIODevice::WriteOnly))
{
auto pDb = pdb.get(tr("exporting JSON"));
sqlite3_stmt* stmt;
int status = sqlite3_prepare_v2(pDb.get(), sQuery.c_str(), static_cast<int>(sQuery.size()), &stmt, nullptr);
json json_table;
if(SQLITE_OK == status)
{
QApplication::setOverrideCursor(Qt::WaitCursor);
int columns = sqlite3_column_count(stmt);
size_t counter = 0;
std::vector<std::string> column_names;
while(sqlite3_step(stmt) == SQLITE_ROW)
{
// Get column names if we didn't do so before
if(!column_names.size())
{
for(int i=0;i<columns;++i)
column_names.push_back(sqlite3_column_name(stmt, i));
}
json json_row;
for(int i=0;i<columns;++i)
{
int type = sqlite3_column_type(stmt, i);
std::string column_name = column_names[static_cast<size_t>(i)];
switch (type) {
case SQLITE_INTEGER: {
qint64 content = sqlite3_column_int64(stmt, i);
json_row[column_name] = content;
break;
}
case SQLITE_FLOAT: {
double content = sqlite3_column_double(stmt, i);
json_row[column_name] = content;
break;
}
case SQLITE_NULL: {
json_row[column_name] = nullptr;
break;
}
case SQLITE_TEXT: {
QString content = QString::fromUtf8(
reinterpret_cast<const char*>(sqlite3_column_text(stmt, i)),
sqlite3_column_bytes(stmt, i));
json_row[column_name] = content.toStdString();
break;
}
case SQLITE_BLOB: {
QByteArray content(reinterpret_cast<const char*>(sqlite3_column_blob(stmt, i)),
sqlite3_column_bytes(stmt, i));
QTextCodec *codec = QTextCodec::codecForName("UTF-8");
QString string = codec->toUnicode(content.toBase64(QByteArray::Base64Encoding));
json_row[column_name] = string.toStdString();
break;
}
}
}
json_table.push_back(json_row);
if(counter % 1000 == 0)
qApp->processEvents();
counter++;
}
}
sqlite3_finalize(stmt);
// Create JSON document
file.write(json_table.dump(ui->checkPrettyPrint->isChecked() ? 4 : -1).c_str());
QApplication::restoreOverrideCursor();
qApp->processEvents();
// Done writing the file
file.close();
} else {
QMessageBox::warning(this, QApplication::applicationName(),
tr("Could not open output file: %1").arg(sFilename));
return false;
}
return true;
}
void ExportDataDialog::accept()
{
QStringList file_dialog_filter;
QString default_file_extension;
switch(m_format)
{
case ExportFormatCsv:
file_dialog_filter << FILE_FILTER_CSV
<< FILE_FILTER_TSV
<< FILE_FILTER_DSV
<< FILE_FILTER_TXT
<< FILE_FILTER_ALL;
default_file_extension = FILE_EXT_CSV_DEFAULT;
break;
case ExportFormatJson:
file_dialog_filter << FILE_FILTER_JSON
<< FILE_FILTER_TXT
<< FILE_FILTER_ALL;
default_file_extension = FILE_EXT_JSON_DEFAULT;
break;
}
if(!m_sQuery.empty())
{
// called from sqlexecute query tab
QString sFilename = FileDialog::getSaveFileName(
CreateDataFile,
this,
tr("Choose a filename to export data"),
file_dialog_filter.join(";;"));
if(sFilename.isEmpty())
{
close();
return;
}
exportQuery(m_sQuery, sFilename);
} else {
// called from the File export menu
QList<QListWidgetItem*> selectedItems = ui->listTables->selectedItems();
if(selectedItems.isEmpty())
{
QMessageBox::warning(this, QApplication::applicationName(),
tr("Please select at least 1 table."));
return;
}
// Get filename
QStringList filenames;
if(selectedItems.size() == 1)
{
QString fileName = FileDialog::getSaveFileName(
CreateDataFile,
this,
tr("Choose a filename to export data"),
file_dialog_filter.join(";;"),
selectedItems.at(0)->text() + default_file_extension);
if(fileName.isEmpty())
{
close();
return;
}
filenames << fileName;
} else {
// ask for folder
QString exportfolder = FileDialog::getExistingDirectory(
CreateDataFile,
this,
tr("Choose a directory"),
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
if(exportfolder.isEmpty())
{
close();
return;
}
for(const QListWidgetItem* item : selectedItems)
filenames << QDir(exportfolder).filePath(item->text() + default_file_extension);
}
// Only if the user hasn't clicked the cancel button
for(int i = 0; i < selectedItems.size(); ++i)
{
// if we are called from execute sql tab, query is already set
// and we only export 1 select
std::string sQuery = "SELECT * FROM " + sqlb::ObjectIdentifier(selectedItems.at(i)->data(Qt::UserRole).toString().toStdString()).toString() + ";";
exportQuery(sQuery, filenames.at(i));
}
}
// Save the dialog preferences for future use
Settings::setValue("exportcsv", "firstrowheader", ui->checkHeader->isChecked());
Settings::setValue("exportjson", "prettyprint", ui->checkPrettyPrint->isChecked());
Settings::setValue("exportcsv", "separator", currentSeparatorChar());
Settings::setValue("exportcsv", "quotecharacter", currentQuoteChar());
Settings::setValue("exportcsv", "newlinecharacters", currentNewLineString());
// Notify the user the export has completed
QMessageBox::information(this, QApplication::applicationName(), tr("Export completed."));
QDialog::accept();
}
void ExportDataDialog::showCustomCharEdits()
{
// Retrieve selection info for the quote, separator, and newline widgets
int quoteIndex = ui->comboQuoteCharacter->currentIndex();
int quoteCount = ui->comboQuoteCharacter->count();
int sepIndex = ui->comboFieldSeparator->currentIndex();
int sepCount = ui->comboFieldSeparator->count();
int newLineIndex = ui->comboNewLineString->currentIndex();
int newLineCount = ui->comboNewLineString->count();
// Determine which will have their 'Other' line edit widget visible
bool quoteVisible = quoteIndex == (quoteCount - 1);
bool sepVisible = sepIndex == (sepCount - 1);
bool newLineVisible = newLineIndex == (newLineCount - 1);
// Update the visibility of the 'Other' line edit widgets
ui->editCustomQuote->setVisible(quoteVisible);
ui->editCustomSeparator->setVisible(sepVisible);
ui->editCustomNewLine->setVisible(newLineVisible);
}
void ExportDataDialog::setQuoteChar(const QChar& c)
{
QComboBox* combo = ui->comboQuoteCharacter;
// Set the combo and/or Other box to the correct selection
switch (c.toLatin1()) {
case '"':
combo->setCurrentIndex(0); // First option is a quote character
break;
case '\'':
combo->setCurrentIndex(1); // Second option is a single quote character
break;
case 0:
combo->setCurrentIndex(2); // Third option is blank (no character)
break;
default:
// For everything else, set the combo box to option 3 ('Other') and
// place the desired string into the matching edit line box
combo->setCurrentIndex(3);
if(!c.isNull())
{
// Don't set it if/when it's the 0 flag value
ui->editCustomQuote->setText(c);
}
break;
}
}
char ExportDataDialog::currentQuoteChar() const
{
QComboBox* combo = ui->comboQuoteCharacter;
switch (combo->currentIndex()) {
case 0:
return '"'; // First option is a quote character
case 1:
return '\''; // Second option is a single quote character
case 2:
return 0; // Third option is a blank (no character)
default:
// The 'Other' option was selected, so check if the matching edit
// line widget contains something
int customQuoteLength = ui->editCustomQuote->text().length();
if (customQuoteLength > 0) {
// Yes it does. Return its first character
char customQuoteChar = ui->editCustomQuote->text().at(0).toLatin1();
return customQuoteChar;
} else {
// No it doesn't, so return 0 to indicate it was empty
return 0;
}
}
}
void ExportDataDialog::setSeparatorChar(const QChar& c)
{
QComboBox* combo = ui->comboFieldSeparator;
// Set the combo and/or Other box to the correct selection
switch (c.toLatin1()) {
case ',':
combo->setCurrentIndex(0); // First option is a comma character
break;
case ';':
combo->setCurrentIndex(1); // Second option is a semi-colon character
break;
case '\t':
combo->setCurrentIndex(2); // Third option is a tab character
break;
case '|':
combo->setCurrentIndex(3); // Fourth option is a pipe symbol
break;
default:
// For everything else, set the combo box to option 3 ('Other') and
// place the desired string into the matching edit line box
combo->setCurrentIndex(4);
// Only put the separator character in the matching line edit box if
// it's not the flag value of 0, which is for indicating its empty
if(!c.isNull())
ui->editCustomSeparator->setText(c);
break;
}
}
char ExportDataDialog::currentSeparatorChar() const
{
QComboBox* combo = ui->comboFieldSeparator;
switch (combo->currentIndex()) {
case 0:
return ','; // First option is a comma character
case 1:
return ';'; // Second option is a semi-colon character
case 2:
return '\t'; // Third option is a tab character
case 3:
return '|'; // Fourth option is a pipe character
default:
// The 'Other' option was selected, so check if the matching edit
// line widget contains something
int customSeparatorLength = ui->editCustomSeparator->text().length();
if (customSeparatorLength > 0) {
// Yes it does. Return its first character
char customSeparatorChar = ui->editCustomSeparator->text().at(0).toLatin1();
return customSeparatorChar;
} else {
// No it doesn't, so return 0 to indicate it was empty
return 0;
}
}
}
void ExportDataDialog::setNewLineString(const QString& s)
{
QComboBox* combo = ui->comboNewLineString;
// Set the combo and/or Other box to the correct selection
if (s == "\r\n") {
// For Windows style newlines, set the combo box to option 0
combo->setCurrentIndex(0);
} else if (s == "\n") {
// For Unix style newlines, set the combo box to option 1
combo->setCurrentIndex(1);
} else {
// For everything else, set the combo box to option 2 ('Other') and
// place the desired string into the matching edit line box
combo->setCurrentIndex(2);
ui->editCustomNewLine->setText(s);
}
}
QString ExportDataDialog::currentNewLineString() const
{
QComboBox* combo = ui->comboNewLineString;
switch (combo->currentIndex()) {
case 0:
// Windows style newlines
return QString("\r\n");
case 1:
// Unix style newlines
return QString("\n");
default:
// Return the text from the 'Other' box
return QString(ui->editCustomNewLine->text().toLatin1());
}
}

View File

@ -0,0 +1,56 @@
#ifndef ExportDataDialog_H
#define ExportDataDialog_H
#include "WBFZExchangePluginAPI.h"
#include <QDialog>
#include "sql/ObjectIdentifier.h"
class DBBrowserDB;
namespace Ui {
class ExportDataDialog;
}
class ExportDataDialog : public QDialog
{
Q_OBJECT
public:
enum ExportFormats
{
ExportFormatCsv,
ExportFormatJson,
};
explicit ExportDataDialog(DBBrowserDB& db, ExportFormats format, QWidget* parent = nullptr,
const std::string& query = {}, const sqlb::ObjectIdentifier& selection = sqlb::ObjectIdentifier());
~ExportDataDialog() override;
private slots:
void accept() override;
void showCustomCharEdits();
private:
void setQuoteChar(const QChar& c);
char currentQuoteChar() const;
void setSeparatorChar(const QChar& c);
char currentSeparatorChar() const;
void setNewLineString(const QString& s);
QString currentNewLineString() const;
bool exportQuery(const std::string& sQuery, const QString& sFilename);
bool exportQueryCsv(const std::string& sQuery, const QString& sFilename);
bool exportQueryJson(const std::string& sQuery, const QString& sFilename);
private:
Ui::ExportDataDialog* ui;
DBBrowserDB& pdb;
ExportFormats m_format;
std::string m_sQuery;
};
#endif

View File

@ -0,0 +1,408 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ExportDataDialog</class>
<widget class="QDialog" name="ExportDataDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>527</width>
<height>381</height>
</rect>
</property>
<property name="windowTitle">
<string>Export data as CSV</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QFormLayout" name="formLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="labelTable">
<property name="text">
<string>Tab&amp;le(s)</string>
</property>
<property name="buddy">
<cstring>listTables</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QListWidget" name="listTables">
<property name="minimumSize">
<size>
<width>360</width>
<height>0</height>
</size>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::MultiSelection</enum>
</property>
<property name="sortingEnabled">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QStackedWidget" name="stackFormat">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="csv">
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="labelHeader">
<property name="text">
<string>Colu&amp;mn names in first line</string>
</property>
<property name="buddy">
<cstring>checkHeader</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="checkHeader">
<property name="text">
<string/>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelFieldSeparator">
<property name="text">
<string>Fie&amp;ld separator</string>
</property>
<property name="buddy">
<cstring>comboFieldSeparator</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QComboBox" name="comboFieldSeparator">
<property name="minimumSize">
<size>
<width>180</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>180</width>
<height>16777215</height>
</size>
</property>
<item>
<property name="text">
<string>,</string>
</property>
</item>
<item>
<property name="text">
<string>;</string>
</property>
</item>
<item>
<property name="text">
<string>Tab</string>
</property>
</item>
<item>
<property name="text">
<string>|</string>
</property>
</item>
<item>
<property name="text">
<string>Other</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLineEdit" name="editCustomSeparator">
<property name="maxLength">
<number>1</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="labelQuoteCharacter">
<property name="text">
<string>&amp;Quote character</string>
</property>
<property name="buddy">
<cstring>comboQuoteCharacter</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QComboBox" name="comboQuoteCharacter">
<property name="minimumSize">
<size>
<width>180</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>180</width>
<height>16777215</height>
</size>
</property>
<item>
<property name="text">
<string>&quot;</string>
</property>
</item>
<item>
<property name="text">
<string>'</string>
</property>
</item>
<item>
<property name="text">
<string/>
</property>
</item>
<item>
<property name="text">
<string>Other</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLineEdit" name="editCustomQuote">
<property name="maxLength">
<number>1</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="3" column="0">
<widget class="QLabel" name="labelNewLineCharacters">
<property name="text">
<string>New line characters</string>
</property>
</widget>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3" stretch="0,0,0">
<item>
<widget class="QComboBox" name="comboNewLineString">
<property name="minimumSize">
<size>
<width>180</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>180</width>
<height>16777215</height>
</size>
</property>
<item>
<property name="text">
<string>Windows: CR+LF (\r\n)</string>
</property>
</item>
<item>
<property name="text">
<string>Unix: LF (\n)</string>
</property>
</item>
<item>
<property name="text">
<string>Other</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLineEdit" name="editCustomNewLine">
<property name="maxLength">
<number>5</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="json">
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Pretty print</string>
</property>
<property name="buddy">
<cstring>checkPrettyPrint</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="checkPrettyPrint"/>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Save</set>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>listTables</tabstop>
<tabstop>checkHeader</tabstop>
<tabstop>comboFieldSeparator</tabstop>
<tabstop>editCustomSeparator</tabstop>
<tabstop>comboQuoteCharacter</tabstop>
<tabstop>editCustomQuote</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>ExportDataDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>263</x>
<y>612</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>140</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>ExportDataDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>263</x>
<y>612</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>140</y>
</hint>
</hints>
</connection>
<connection>
<sender>comboFieldSeparator</sender>
<signal>currentIndexChanged(int)</signal>
<receiver>ExportDataDialog</receiver>
<slot>showCustomCharEdits()</slot>
<hints>
<hint type="sourcelabel">
<x>390</x>
<y>293</y>
</hint>
<hint type="destinationlabel">
<x>264</x>
<y>45</y>
</hint>
</hints>
</connection>
<connection>
<sender>comboQuoteCharacter</sender>
<signal>currentIndexChanged(int)</signal>
<receiver>ExportDataDialog</receiver>
<slot>showCustomCharEdits()</slot>
<hints>
<hint type="sourcelabel">
<x>390</x>
<y>332</y>
</hint>
<hint type="destinationlabel">
<x>270</x>
<y>85</y>
</hint>
</hints>
</connection>
<connection>
<sender>comboNewLineString</sender>
<signal>currentIndexChanged(int)</signal>
<receiver>ExportDataDialog</receiver>
<slot>showCustomCharEdits()</slot>
<hints>
<hint type="sourcelabel">
<x>390</x>
<y>371</y>
</hint>
<hint type="destinationlabel">
<x>296</x>
<y>248</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>showCustomCharEdits()</slot>
</slots>
</ui>

View File

@ -0,0 +1,133 @@
#include "ExportSqlDialog.h"
#include "ui_ExportSqlDialog.h"
#include "sqlitedb.h"
#include "FileDialog.h"
#include "Settings.h"
#include "IconCache.h"
#include <QFile>
#include <QMessageBox>
enum WhatComboEntries
{
ExportEverything,
ExportSchemaOnly,
ExportDataOnly,
};
ExportSqlDialog::ExportSqlDialog(DBBrowserDB* db, QWidget* parent, const QString& selection)
: QDialog(parent),
ui(new Ui::ExportSqlDialog),
pdb(db)
{
// Create UI
ui->setupUi(this);
// Load settings
ui->checkColNames->setChecked(Settings::getValue("exportsql", "insertcolnames").toBool());
ui->checkMultiple->setChecked(Settings::getValue("exportsql", "insertmultiple").toBool());
ui->comboOldSchema->setCurrentIndex(Settings::getValue("exportsql", "oldschema").toInt());
// Get list of tables to export
const auto objects = pdb->schemata["main"].equal_range("table");
for(auto it=objects.first;it!=objects.second;++it)
ui->listTables->addItem(new QListWidgetItem(IconCache::get(sqlb::Object::typeToString(it->second->type())), QString::fromStdString(it->second->name())));
// Sort list of tables and select the table specified in the
// selection parameter or all tables if table not specified
ui->listTables->model()->sort(0);
if(selection.isEmpty())
{
for (int i = 0; i < ui->listTables->count(); ++i)
ui->listTables->item(i)->setSelected(true);
}
else
{
QList<QListWidgetItem*> items = ui->listTables->findItems(selection, Qt::MatchExactly);
ui->listTables->setCurrentItem(items.at(0));
}
ui->listTables->setFocus();
}
ExportSqlDialog::~ExportSqlDialog()
{
delete ui;
}
void ExportSqlDialog::doSelectAll()
{
for (int i = 0; i < ui->listTables->count(); ++i)
ui->listTables->item(i)->setSelected(true);
}
void ExportSqlDialog::doDeselectAll()
{
for (int i = 0; i < ui->listTables->count(); ++i)
ui->listTables->item(i)->setSelected(false);
}
void ExportSqlDialog::accept()
{
QList<QListWidgetItem*> selectedItems = ui->listTables->selectedItems();
if(selectedItems.isEmpty())
{
QMessageBox::warning(this, QApplication::applicationName(),
tr("Please select at least one table."));
return;
}
// Try to find a default file name
QString defaultFileName;
if(selectedItems.count() == 1) // One table -> Suggest table name
defaultFileName = selectedItems.at(0)->text() + FILE_EXT_SQL_DEFAULT;
else if(selectedItems.count() == ui->listTables->count()) // All tables -> Suggest database name
defaultFileName = pdb->currentFile() + FILE_EXT_SQL_DEFAULT;
QString fileName = FileDialog::getSaveFileName(
CreateSQLFile,
this,
tr("Choose a filename to export"),
FILE_FILTER_SQL,
defaultFileName);
if(fileName.isEmpty())
return;
// Save settings
Settings::setValue("exportsql", "insertcolnames", ui->checkColNames->isChecked());
Settings::setValue("exportsql", "insertmultiple", ui->checkMultiple->isChecked());
Settings::setValue("exportsql", "oldschema", ui->comboOldSchema->currentIndex());
std::vector<std::string> tables;
for(const QListWidgetItem* item : ui->listTables->selectedItems())
tables.push_back(item->text().toStdString());
// Check what to export. The indices here depend on the order of the items in the combobox in the ui file
bool exportSchema = ui->comboWhat->currentIndex() == ExportEverything || ui->comboWhat->currentIndex() == ExportSchemaOnly;
bool exportData = ui->comboWhat->currentIndex() == ExportEverything || ui->comboWhat->currentIndex() == ExportDataOnly;
bool keepSchema = ui->comboOldSchema->currentIndex() == 0;
// Perform actual export
bool dumpOk = pdb->dump(fileName,
tables,
ui->checkColNames->isChecked(),
ui->checkMultiple->isChecked(),
exportSchema,
exportData,
keepSchema);
if (dumpOk)
QMessageBox::information(this, QApplication::applicationName(), tr("Export completed."));
else
QMessageBox::warning(this, QApplication::applicationName(), tr("Export cancelled or failed."));
QDialog::accept();
}
void ExportSqlDialog::whatChanged(int index)
{
// Only show the combobox for deciding what to do with the old schema when importing into an existing database when we're
// actually exporting the schema here
if(index != ExportDataOnly)
ui->comboOldSchema->setVisible(true);
else
ui->comboOldSchema->setVisible(false);
}

View File

@ -0,0 +1,31 @@
#ifndef ExportSqlDialog_H
#define ExportSqlDialog_H
#include "WBFZExchangePluginAPI.h"
#include <QDialog>
class DBBrowserDB;
namespace Ui {
class ExportSqlDialog;
}
class ExportSqlDialog : public QDialog
{
Q_OBJECT
public:
explicit ExportSqlDialog(DBBrowserDB* db, QWidget* parent = nullptr, const QString& selection = QString());
~ExportSqlDialog() override;
private slots:
void accept() override;
void doSelectAll();
void doDeselectAll();
void whatChanged(int index);
private:
Ui::ExportSqlDialog* ui;
DBBrowserDB* pdb;
};
#endif

View File

@ -0,0 +1,242 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ExportSqlDialog</class>
<widget class="QDialog" name="ExportSqlDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>497</width>
<height>352</height>
</rect>
</property>
<property name="windowTitle">
<string>Export SQL...</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="2" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Save</set>
</property>
</widget>
</item>
<item row="0" column="0">
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="labelTable">
<property name="text">
<string>Tab&amp;le(s)</string>
</property>
<property name="buddy">
<cstring>listTables</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QListWidget" name="listTables">
<property name="selectionMode">
<enum>QAbstractItemView::MultiSelection</enum>
</property>
<property name="sortingEnabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<property name="topMargin">
<number>10</number>
</property>
<item>
<widget class="QPushButton" name="buttonSelectAll">
<property name="text">
<string>Select All</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonDeselectAll">
<property name="text">
<string>Deselect All</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>&amp;Options</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QCheckBox" name="checkColNames">
<property name="text">
<string>Keep column names in INSERT INTO</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="checkMultiple">
<property name="text">
<string>Multiple rows (VALUES) per INSERT statement</string>
</property>
</widget>
</item>
<item row="4" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="0">
<widget class="QComboBox" name="comboWhat">
<item>
<property name="text">
<string>Export everything</string>
</property>
</item>
<item>
<property name="text">
<string>Export schema only</string>
</property>
</item>
<item>
<property name="text">
<string>Export data only</string>
</property>
</item>
</widget>
</item>
<item row="3" column="0">
<widget class="QComboBox" name="comboOldSchema">
<item>
<property name="text">
<string>Keep old schema (CREATE TABLE IF NOT EXISTS)</string>
</property>
</item>
<item>
<property name="text">
<string>Overwrite old schema (DROP TABLE, then CREATE TABLE)</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<tabstops>
<tabstop>listTables</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>ExportSqlDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>258</x>
<y>331</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>140</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonDeselectAll</sender>
<signal>clicked()</signal>
<receiver>ExportSqlDialog</receiver>
<slot>doDeselectAll()</slot>
<hints>
<hint type="sourcelabel">
<x>488</x>
<y>129</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonSelectAll</sender>
<signal>clicked()</signal>
<receiver>ExportSqlDialog</receiver>
<slot>doSelectAll()</slot>
<hints>
<hint type="sourcelabel">
<x>140</x>
<y>129</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>ExportSqlDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>317</y>
</hint>
<hint type="destinationlabel">
<x>248</x>
<y>168</y>
</hint>
</hints>
</connection>
<connection>
<sender>comboWhat</sender>
<signal>currentIndexChanged(int)</signal>
<receiver>ExportSqlDialog</receiver>
<slot>whatChanged(int)</slot>
<hints>
<hint type="sourcelabel">
<x>320</x>
<y>234</y>
</hint>
<hint type="destinationlabel">
<x>492</x>
<y>226</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>showCustomCharEdits()</slot>
<slot>whatChanged(int)</slot>
</slots>
</ui>

View File

@ -0,0 +1,325 @@
#include "ExtendedScintilla.h"
#include "FindReplaceDialog.h"
#include "Settings.h"
#include "Qsci/qscilexer.h"
#include "Qsci/qsciprinter.h"
#ifdef Q_OS_MACX
#include <Qsci/qscicommandset.h>
#include <Qsci/qscicommand.h>
#endif
#include <QFile>
#include <QDropEvent>
#include <QUrl>
#include <QMimeData>
#include <QShortcut>
#include <QAction>
#include <QMenu>
#include <QPalette>
#include <QPrintPreviewDialog>
#include <cmath>
ExtendedScintilla::ExtendedScintilla(QWidget* parent) :
QsciScintilla(parent),
showErrorIndicators(true),
findReplaceDialog(new FindReplaceDialog(this))
{
// This class does not set any lexer, that must be done in the child classes.
// Enable UTF8
setUtf8(true);
// Enable brace matching
setBraceMatching(QsciScintilla::SloppyBraceMatch);
// Enable auto indentation
setAutoIndent(true);
// Enable folding
setFolding(QsciScintilla::BoxedTreeFoldStyle);
// Create error indicator
errorIndicatorNumber = indicatorDefine(QsciScintilla::SquiggleIndicator);
setIndicatorForegroundColor(Qt::red, errorIndicatorNumber);
// Set a sensible scroll width, so the scroll bar is avoided in
// most cases.
setScrollWidth(80);
// Scroll width is adjusted to ensure that all of the lines
// currently displayed can be completely scrolled. This mode never
// adjusts the scroll width to be narrower.
setScrollWidthTracking(true);
// Visual flags for when wrap lines is enabled
setWrapVisualFlags(QsciScintilla::WrapFlagByBorder);
// Connect signals
connect(this, &ExtendedScintilla::linesChanged, this, &ExtendedScintilla::updateLineNumberAreaWidth);
// The shortcuts are constrained to the Widget context so they do not conflict with other SqlTextEdit widgets in the Main Window.
QShortcut* shortcutFindReplace = new QShortcut(QKeySequence(tr("Ctrl+H")), this, nullptr, nullptr, Qt::WidgetShortcut);
connect(shortcutFindReplace, &QShortcut::activated, this, &ExtendedScintilla::openFindReplaceDialog);
shortcutFind = new QShortcut(QKeySequence(tr("Ctrl+F")), this, nullptr, nullptr, Qt::WidgetShortcut);
connect(shortcutFind, &QShortcut::activated, this, &ExtendedScintilla::openFindDialog);
#ifdef Q_OS_MACX
// Alt+Backspace on Mac is expected to delete one word to the left,
// instead of undoing (default Scintilla binding).
QsciCommand * command = standardCommands()->find(QsciCommand::DeleteWordLeft);
command->setKey(Qt::AltModifier+Qt::Key_Backspace);
// And Cmd+Backspace should delete from cursor to the beginning of line
command = standardCommands()->find(QsciCommand::DeleteLineLeft);
command->setKey(Qt::ControlModifier+Qt::Key_Backspace);
#endif
QShortcut* shortcutPrint = new QShortcut(QKeySequence(tr("Ctrl+P")), this, nullptr, nullptr, Qt::WidgetShortcut);
connect(shortcutPrint, &QShortcut::activated, this, &ExtendedScintilla::openPrintDialog);
// Prepare for adding the find/replace option to the QScintilla context menu
setContextMenuPolicy(Qt::CustomContextMenu);
connect(this, &ExtendedScintilla::customContextMenuRequested, this, &ExtendedScintilla::showContextMenu);
}
void ExtendedScintilla::updateLineNumberAreaWidth()
{
// Calculate number of digits of the current number of lines
int digits = static_cast<int>(std::log10(lines())) + 1;
// Calculate the width of this number if it was all zeros (this is because a 1 might require less space than a 0 and this could
// cause some flickering depending on the font) and set the new margin width.
setMarginWidth(0, QFontMetrics(font()).width(QString("0").repeated(digits)) + 5);
}
void ExtendedScintilla::dropEvent(QDropEvent* e)
{
QList<QUrl> urls = e->mimeData()->urls();
if(urls.isEmpty())
return QsciScintilla::dropEvent(e);
QString file = urls.first().toLocalFile();
if(!QFile::exists(file))
return;
QFile f(file);
f.open(QIODevice::ReadOnly);
setText(f.readAll());
f.close();
}
void ExtendedScintilla::setupSyntaxHighlightingFormat(QsciLexer* lexer, const std::string& settings_name, int style)
{
lexer->setColor(QColor(Settings::getValue("syntaxhighlighter", settings_name + "_colour").toString()), style);
QFont font(Settings::getValue("editor", "font").toString());
font.setPointSize(Settings::getValue("editor", "fontsize").toInt());
font.setBold(Settings::getValue("syntaxhighlighter", settings_name + "_bold").toBool());
font.setItalic(Settings::getValue("syntaxhighlighter", settings_name + "_italic").toBool());
font.setUnderline(Settings::getValue("syntaxhighlighter", settings_name + "_underline").toBool());
lexer->setFont(font, style);
}
void ExtendedScintilla::setLexer(QsciLexer *lexer)
{
QsciScintilla::setLexer(lexer);
reloadCommonSettings();
}
void ExtendedScintilla::reloadCommonSettings()
{
// Set margins and default text colours according to settings. setLexer seems to reset these colours.
// Use desktop default colors for margins when following desktop
// style, or the colors matching the dark style-sheet, otherwise.
switch (Settings::getValue("General", "appStyle").toInt()) {
case Settings::FollowDesktopStyle :
setMarginsBackgroundColor(QPalette().color(QPalette::Active, QPalette::Window));
setMarginsForegroundColor(QPalette().color(QPalette::Active, QPalette::WindowText));
break;
case Settings::DarkStyle :
setMarginsBackgroundColor(QColor("#32414B"));
setMarginsForegroundColor(QColor("#EFF0F1"));
break;
}
setPaper(Settings::getValue("syntaxhighlighter", "background_colour").toString());
setColor(Settings::getValue("syntaxhighlighter", "foreground_colour").toString());
}
void ExtendedScintilla::reloadKeywords()
{
// Set lexer again to reload the updated keywords list
setLexer(lexer());
}
void ExtendedScintilla::reloadSettings()
{
reloadLexerSettings(lexer());
reloadKeywords();
}
void ExtendedScintilla::reloadLexerSettings(QsciLexer *lexer)
{
QColor foreground (Settings::getValue("syntaxhighlighter", "foreground_colour").toString());
QColor background (Settings::getValue("syntaxhighlighter", "background_colour").toString());
QFont defaultfont(Settings::getValue("editor", "font").toString());
defaultfont.setStyleHint(QFont::TypeWriter);
defaultfont.setPointSize(Settings::getValue("editor", "fontsize").toInt());
// Set syntax highlighting settings
if(lexer)
{
lexer->setFont(defaultfont);
lexer->setDefaultPaper(background);
lexer->setDefaultColor(foreground);
// This sets the base colors for all the styles
lexer->setPaper(background);
lexer->setColor(foreground);
}
// Set font
setFont(defaultfont);
// Show line numbers
setMarginsFont(defaultfont);
setMarginLineNumbers(0, true);
updateLineNumberAreaWidth();
// Highlight current line
setCaretLineVisible(true);
setCaretLineBackgroundColor(QColor(Settings::getValue("syntaxhighlighter", "currentline_colour").toString()));
setCaretForegroundColor(foreground);
// Set tab width
setTabWidth(Settings::getValue("editor", "tabsize").toInt());
if(lexer)
lexer->refreshProperties();
// Check if error indicators are enabled and clear them if they just got disabled
showErrorIndicators = Settings::getValue("editor", "error_indicators").toBool();
if(!showErrorIndicators)
clearErrorIndicators();
}
void ExtendedScintilla::clearErrorIndicators()
{
// Clear any error indicators from position (0,0) to the last column of the last line
clearIndicatorRange(0, 0, lines(), lineLength(lines()), errorIndicatorNumber);
}
void ExtendedScintilla::setErrorIndicator(int fromRow, int fromIndex, int toRow, int toIndex)
{
// Set error indicator for the specified range but only if they're enabled
if(showErrorIndicators)
fillIndicatorRange(fromRow, fromIndex, toRow, toIndex, errorIndicatorNumber);
}
void ExtendedScintilla::setErrorIndicator(int position)
{
// Set error indicator for the position until end of line, but only if they're enabled
if(showErrorIndicators) {
int column = static_cast<int>(SendScintilla(QsciScintillaBase::SCI_GETCOLUMN, position));
int line = static_cast<int>(SendScintilla(QsciScintillaBase::SCI_LINEFROMPOSITION, position));
fillIndicatorRange(line, column, line+1, 0, errorIndicatorNumber);
}
}
bool ExtendedScintilla::findText(QString text, bool regexp, bool caseSensitive, bool words, bool wrap, bool forward) {
// For finding the previous occurrence, we need to skip the current
// selection, otherwise we'd always found the same occurrence.
if (!forward && hasSelectedText()) {
int lineFrom, indexFrom;
int lineTo, indexTo;
getSelection(&lineFrom, &indexFrom, &lineTo, &indexTo);
setCursorPosition(lineFrom, indexFrom);
}
return findFirst(text, regexp, caseSensitive, words, wrap, forward,
/* line */ -1, /* index */ -1,
/* show */ true, /* posix */ true, /* cxx11 */ true);
}
void ExtendedScintilla::setEnabledFindDialog(bool value)
{
shortcutFind->setEnabled(value);
}
void ExtendedScintilla::clearSelection()
{
setSelection(-1,-1,-1,-1);
}
void ExtendedScintilla::openFindReplaceDialog()
{
findReplaceDialog->setExtendedScintilla(this);
findReplaceDialog->showFindReplaceDialog(true);
}
void ExtendedScintilla::openFindDialog()
{
findReplaceDialog->setExtendedScintilla(this);
findReplaceDialog->showFindReplaceDialog(false);
}
void ExtendedScintilla::showContextMenu(const QPoint &pos)
{
// This has to be created here, otherwise the set of enabled options would not update accordingly.
QMenu* editContextMenu = createStandardContextMenu();
editContextMenu->addSeparator();
if (shortcutFind->isEnabled()) {
QAction* findAction = new QAction(QIcon(":/icons/find"), tr("Find..."), this);
findAction->setShortcut(shortcutFind->key());
connect(findAction, &QAction::triggered, this, &ExtendedScintilla::openFindDialog);
editContextMenu->addAction(findAction);
}
QAction* findReplaceAction = new QAction(QIcon(":/icons/text_replace"), tr("Find and Replace..."), this);
findReplaceAction->setShortcut(QKeySequence(tr("Ctrl+H")));
connect(findReplaceAction, &QAction::triggered, this, &ExtendedScintilla::openFindReplaceDialog);
QAction* printAction = new QAction(QIcon(":/icons/print"), tr("Print..."), this);
printAction->setShortcut(QKeySequence(tr("Ctrl+P")));
connect(printAction, &QAction::triggered, this, &ExtendedScintilla::openPrintDialog);
editContextMenu->addAction(findReplaceAction);
editContextMenu->addSeparator();
editContextMenu->addAction(printAction);
editContextMenu->exec(mapToGlobal(pos));
}
void ExtendedScintilla::openPrintDialog()
{
QsciPrinter printer;
QPrintPreviewDialog *dialog = new QPrintPreviewDialog(&printer);
connect(dialog, &QPrintPreviewDialog::paintRequested, [&](QPrinter *previewPrinter) {
QsciPrinter* sciPrinter = static_cast<QsciPrinter*>(previewPrinter);
sciPrinter->printRange(this);
});
dialog->exec();
delete dialog;
}
void ExtendedScintilla::setReadOnly(bool ro)
{
QsciScintilla::setReadOnly(ro);
// Disable or enable caret blinking so it is obvious whether the text can be modified or not. Otherwise there isn't any other hint.
SendScintilla(QsciScintillaBase::SCI_SETCARETPERIOD, ro ? 0 : 500);
}
void ExtendedScintilla::setText(const QString& text)
{
// Reset scroll width, so the scroll bar is readjusted to the new text.
// Otherwise, it grows always bigger.
setScrollWidth(80);
QsciScintilla::setText(text);
}

View File

@ -0,0 +1,58 @@
#ifndef EXTENDEDSCINTILLA_H
#define EXTENDEDSCINTILLA_H
#include "WBFZExchangePluginAPI.h"
#include "Qsci/qsciscintilla.h"
class FindReplaceDialog;
class QShortcut;
/**
* @brief The ExtendedScintilla class
* This class extends the QScintilla widget for the application
*/
class ExtendedScintilla : public QsciScintilla
{
Q_OBJECT
public:
explicit ExtendedScintilla(QWidget *parent = nullptr);
bool findText(QString text, bool regexp, bool caseSensitive, bool words, bool wrap, bool forward);
void setEnabledFindDialog(bool value);
void clearSelection();
// Override parent setLexer
void setLexer(QsciLexer *lexer) override;
// Override parent setReadOnly
void setReadOnly(bool ro) override;
// Override parent setText
void setText(const QString& text) override;
public slots:
void reloadKeywords();
void reloadSettings();
void clearErrorIndicators();
void setErrorIndicator(int fromRow, int fromIndex, int toRow, int toIndex);
// Set error indicator from position to end of line
void setErrorIndicator(int position);
void openFindReplaceDialog();
void openFindDialog();
void openPrintDialog();
protected:
void dropEvent(QDropEvent* e) override;
void setupSyntaxHighlightingFormat(QsciLexer* lexer, const std::string& settings_name, int style);
void reloadLexerSettings(QsciLexer *lexer);
void reloadCommonSettings();
int errorIndicatorNumber;
bool showErrorIndicators;
FindReplaceDialog* findReplaceDialog;
QShortcut* shortcutFind;
private slots:
void updateLineNumberAreaWidth();
void showContextMenu(const QPoint &pos);
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,110 @@
#ifndef EXTENDEDTABLEWIDGET_H
#define EXTENDEDTABLEWIDGET_H
#include "WBFZExchangePluginAPI.h"
#include <QTableView>
#include <QStyledItemDelegate>
#include <QSortFilterProxyModel>
#include <unordered_set>
#include "sql/Query.h"
class QMenu;
class QMimeData;
class QDropEvent;
class QDragMoveEvent;
class FilterTableHeader;
namespace sqlb { class ObjectIdentifier; }
// Filter proxy model that only accepts distinct non-empty values.
class UniqueFilterModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
explicit UniqueFilterModel(QObject* parent = nullptr);
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
private:
std::unordered_set<std::string> m_uniqueValues;
};
// We use this class to provide editor widgets for the ExtendedTableWidget. It's used for every cell in the table view.
class ExtendedTableWidgetEditorDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
explicit ExtendedTableWidgetEditorDelegate(QObject* parent = nullptr);
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
void setEditorData(QWidget* editor, const QModelIndex& index) const override;
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override;
void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
};
class ExtendedTableWidget : public QTableView
{
Q_OBJECT
public:
explicit ExtendedTableWidget(QWidget* parent = nullptr);
FilterTableHeader* filterHeader() { return m_tableHeader; }
public:
// Get set of selected columns (all cells in column has to be selected)
std::unordered_set<size_t> selectedCols() const;
// Get set of columns traversed by selection (only some cells in column has to be selected)
std::unordered_set<size_t> colsInSelection() const;
int numVisibleRows() const;
void sortByColumns(const std::vector<sqlb::SortedColumn>& columns);
public slots:
void reloadSettings();
void selectTableLine(int lineToSelect);
void selectTableLines(int firstLine, int count);
void selectAll() override;
void openPrintDialog();
signals:
void foreignKeyClicked(const sqlb::ObjectIdentifier& table, const std::string& column, const QByteArray& value);
void switchTable(bool next); // 'next' parameter is set to true if next table should be selected and to false if previous table should be selected
void openFileFromDropEvent(QString);
void selectedRowsToBeDeleted();
void editCondFormats(int column);
void currentIndexChanged(const QModelIndex &current, const QModelIndex &previous);
void requestUrlOrFileOpen(const QString& urlString);
private:
void copyMimeData(const QModelIndexList& fromIndices, QMimeData* mimeData, const bool withHeaders, const bool inSQL);
void copy(const bool withHeaders, const bool inSQL);
void paste();
void useAsFilter(const QString& filterOperator, bool binary = false, const QString& operatorSuffix = QString());
void duplicateUpperCell();
void setToNull(const QModelIndexList& indices);
static std::vector<std::vector<QByteArray>> m_buffer;
static QString m_generatorStamp;
private slots:
void vscrollbarChanged(int value);
void cellClicked(const QModelIndex& index);
protected:
void keyPressEvent(QKeyEvent* event) override;
void updateGeometries() override;
void dragEnterEvent(QDragEnterEvent* event) override;
void dragMoveEvent(QDragMoveEvent* event) override;
void dropEvent(QDropEvent* event) override;
void currentChanged(const QModelIndex &current, const QModelIndex &previous) override;
FilterTableHeader* m_tableHeader;
QMenu* m_contextMenu;
ExtendedTableWidgetEditorDelegate* m_editorDelegate;
};
#endif

View File

@ -0,0 +1,83 @@
#include "FileDialog.h"
#include "Settings.h"
QString FileDialog::getOpenFileName(const FileDialogTypes dialogType, QWidget* parent, const QString& caption, const QString &filter, QString *selectedFilter, Options options)
{
QString result = QFileDialog::getOpenFileName(parent, caption, getFileDialogPath(dialogType), filter, selectedFilter, options);
if(!result.isEmpty())
setFileDialogPath(dialogType, result);
return result;
}
QStringList FileDialog::getOpenFileNames(const FileDialogTypes dialogType, QWidget *parent, const QString &caption, const QString &filter, QString *selectedFilter, QFileDialog::Options options)
{
QStringList result = QFileDialog::getOpenFileNames(parent, caption, getFileDialogPath(dialogType), filter, selectedFilter, options);
if(!result.isEmpty())
{
QFileInfo path = QFileInfo(result.first());
setFileDialogPath(dialogType, path.absolutePath());
}
return result;
}
QString FileDialog::getSaveFileName(const FileDialogTypes dialogType, QWidget* parent, const QString& caption, const QString& filter, const QString& defaultFileName, QString* selectedFilter, Options options)
{
QString dir = getFileDialogPath(dialogType);
if(!defaultFileName.isEmpty())
dir += "/" + defaultFileName;
QString result = QFileDialog::getSaveFileName(parent, caption, dir, filter, selectedFilter, options);
if(!result.isEmpty())
setFileDialogPath(dialogType, result);
return result;
}
QString FileDialog::getExistingDirectory(const FileDialogTypes dialogType, QWidget* parent, const QString& caption, Options options)
{
QString result = QFileDialog::getExistingDirectory(parent, caption, getFileDialogPath(dialogType), options);
if(!result.isEmpty())
setFileDialogPath(dialogType, result);
return result;
}
QString FileDialog::getFileDialogPath(const FileDialogTypes dialogType)
{
switch(Settings::getValue("db", "savedefaultlocation").toInt())
{
case 0: // Remember last location
case 2: { // Remember last location for current session only
QHash<QString, QVariant> lastLocations = Settings::getValue("db", "lastlocations").toHash();
return lastLocations[QString(dialogType)].toString();
}
case 1: // Always use this locations
return Settings::getValue("db", "defaultlocation").toString();
default:
return QString();
}
}
void FileDialog::setFileDialogPath(const FileDialogTypes dialogType, const QString& new_path)
{
QString dir = QFileInfo(new_path).absolutePath();
QHash<QString, QVariant> lastLocations = Settings::getValue("db", "lastlocations").toHash();
lastLocations[QString(dialogType)] = dir;
switch(Settings::getValue("db", "savedefaultlocation").toInt())
{
case 0: // Remember last location
Settings::setValue("db", "lastlocations", lastLocations);
break;
case 2: // Remember last location for current session only
Settings::setValue("db", "lastlocations", lastLocations, true);
break;
case 1: // Always use this locations
break; // Do nothing
}
}
QString FileDialog::getSqlDatabaseFileFilter()
{
return Settings::getValue("General", "DBFileExtensions").toString() + ";;" + QObject::tr("All files (*)"); //Always add "All files (*)" to the available filters
}

View File

@ -0,0 +1,104 @@
#ifndef FILEDIALOG_H
#define FILEDIALOG_H
#include "WBFZExchangePluginAPI.h"
#include <QFileDialog>
//
// File Extensions Filters
// - space separated list of file extensions in QString
// - used during import/export
// - passed to QFileDialog to filter files shown based on extension
//
// SQLite DB File Extensions
static const QString FILE_FILTER_SQLDB(QObject::tr("SQLite Database Files (*.db *.sqlite *.sqlite3 *.db3)"));
// SQLite DB Project File Extensions
static const QString FILE_FILTER_SQLPRJ(QObject::tr("DB Browser for SQLite Project Files (*.sqbpro)"));
static const QString FILE_EXT_SQLPRJ_DEFAULT(".sqbpro");
// SQL File Extensions Filter
static const QString FILE_FILTER_SQL(QObject::tr("SQL Files (*.sql)"));
static const QString FILE_EXT_SQL_DEFAULT(".sql");
// All Files Extensions Filter
static const QString FILE_FILTER_ALL(QObject::tr("All Files (*)"));
// Text Files Extensions Filter
static const QString FILE_FILTER_TXT(QObject::tr("Text Files (*.txt)"));
static const QString FILE_EXT_TXT_DEFAULT(".txt");
// Comma,Tab,or Delimiter-Separated Values File Extensions Filter
static const QString FILE_FILTER_CSV(QObject::tr("Comma-Separated Values Files (*.csv)"));
static const QString FILE_EXT_CSV_DEFAULT(".csv");
static const QString FILE_FILTER_TSV(QObject::tr("Tab-Separated Values Files (*.tsv)"));
static const QString FILE_FILTER_DSV(QObject::tr("Delimiter-Separated Values Files (*.dsv)"));
static const QString FILE_FILTER_DAT(QObject::tr("Concordance DAT files (*.dat)"));
// JSON File Extensions Filter
static const QString FILE_FILTER_JSON(QObject::tr("JSON Files (*.json *.js)"));
static const QString FILE_EXT_JSON_DEFAULT(".json");
// XML File Extensions Filter
static const QString FILE_FILTER_XML(QObject::tr("XML Files (*.xml)"));
static const QString FILE_EXT_XML_DEFAULT(".xml");
// Binary File Extensions Filter
static const QString FILE_FILTER_BIN(QObject::tr("Binary Files (*.bin *.dat)"));
static const QString FILE_EXT_BIN_DEFAULT(".bin");
// Scalar Vector Graphics File Extensions Filter
static const QString FILE_FILTER_SVG(QObject::tr("SVG Files (*.svg)"));
static const QString FILE_EXT_SVG_DEFAULT(".svg");
// Hex-Dump File Extension Filter
static const QString FILE_FILTER_HEX(QObject::tr("Hex Dump Files (*.dat *.bin)"));
// Dynamic/Shared Objects File Extension Filter
static const QString FILE_FILTER_DYN(QObject::tr("Extensions (*.so *.dylib *.dll)"));
enum FileDialogTypes {
NoSpecificType,
CreateProjectFile,
OpenProjectFile,
CreateDatabaseFile,
OpenDatabaseFile,
CreateSQLFile,
OpenSQLFile,
OpenCSVFile,
CreateDataFile,
OpenDataFile,
OpenExtensionFile,
OpenCertificateFile
};
class FileDialog : public QFileDialog
{
Q_OBJECT
public:
static QString getOpenFileName(const FileDialogTypes dialogType, QWidget* parent = nullptr, const QString& caption = QString(),
const QString& filter = QString(), QString* selectedFilter = nullptr,
Options options = Options());
static QStringList getOpenFileNames(const FileDialogTypes dialogType, QWidget* parent = nullptr, const QString& caption = QString(),
const QString& filter = QString(), QString* selectedFilter = nullptr,
Options options = Options());
static QString getSaveFileName(const FileDialogTypes dialogType, QWidget* parent = nullptr, const QString& caption = QString(),
const QString& filter = QString(), const QString& defaultFileName = QString(), QString* selectedFilter = nullptr,
Options options = Options());
static QString getExistingDirectory(const FileDialogTypes dialogType, QWidget* parent = nullptr, const QString& caption = QString(),
Options options = Options());
static QString getSqlDatabaseFileFilter();
private:
static QString getFileDialogPath(const FileDialogTypes dialogType);
static void setFileDialogPath(const FileDialogTypes dialogType, const QString& new_path);
};
#endif

View File

@ -0,0 +1,104 @@
#include "FileExtensionManager.h"
#include "ui_FileExtensionManager.h"
#include <set>
FileExtensionManager::FileExtensionManager(QStringList init, QWidget *parent) :
QDialog(parent),
ui(new Ui::FileExtensionManager)
{
ui->setupUi(this);
int i = 0;
for(const QString& itemString : init)
{
QString description = itemString.left(itemString.indexOf('(')).trimmed();
QString extension = itemString;
extension = extension.remove (0, itemString.indexOf('(')+1).remove(')').simplified().trimmed();
QTableWidgetItem *newItemDescription = new QTableWidgetItem(description);
QTableWidgetItem *newItemExtension = new QTableWidgetItem(extension);
ui->tableExtensions->insertRow(i);
ui->tableExtensions->setItem(i, 0, newItemDescription);
ui->tableExtensions->setItem(i, 1, newItemExtension);
i++;
}
connect(ui->buttonAdd, SIGNAL(clicked(bool)), this, SLOT(addItem()));
connect(ui->buttonRemove, SIGNAL(clicked(bool)), this, SLOT(removeItem()));
connect(ui->buttonDown, SIGNAL(clicked(bool)), this, SLOT(downItem()));
connect(ui->buttonUp, SIGNAL(clicked(bool)), this, SLOT(upItem()));
}
FileExtensionManager::~FileExtensionManager()
{
delete ui;
}
void FileExtensionManager::addItem()
{
int i = ui->tableExtensions->rowCount();
ui->tableExtensions->insertRow(i);
QTableWidgetItem *newItemDescription = new QTableWidgetItem(tr("Description"));
QTableWidgetItem *newItemExtension = new QTableWidgetItem(tr("*.extension"));
ui->tableExtensions->setItem(i, 0, newItemDescription);
ui->tableExtensions->setItem(i, 1, newItemExtension);
}
void FileExtensionManager::removeItem()
{
std::set<int> selectedRows;
for (const QTableWidgetItem* item : ui->tableExtensions->selectedItems())
selectedRows.insert(item->row());
for(int row : selectedRows)
ui->tableExtensions->removeRow(row);
}
void FileExtensionManager::upItem()
{
if (ui->tableExtensions->selectedItems().isEmpty()) return;
int selectedRow = ui->tableExtensions->selectedItems().first()->row();
if(selectedRow == 0)
return;
QTableWidgetItem *t1, *t2;
t1 = ui->tableExtensions->takeItem(selectedRow, 0);
t2 = ui->tableExtensions->takeItem(selectedRow, 1);
ui->tableExtensions->removeRow(selectedRow);
ui->tableExtensions->insertRow(selectedRow-1);
ui->tableExtensions->setItem(selectedRow-1, 0, t1);
ui->tableExtensions->setItem(selectedRow-1, 1, t2);
ui->tableExtensions->selectRow(selectedRow-1);
}
void FileExtensionManager::downItem()
{
if (ui->tableExtensions->selectedItems().isEmpty()) return;
int selectedRow = ui->tableExtensions->selectedItems().first()->row();
if(selectedRow == ui->tableExtensions->rowCount() - 1)
return;
QTableWidgetItem *t1, *t2;
t1 = ui->tableExtensions->takeItem(selectedRow, 0);
t2 = ui->tableExtensions->takeItem(selectedRow, 1);
ui->tableExtensions->removeRow(selectedRow);
ui->tableExtensions->insertRow(selectedRow+1);
ui->tableExtensions->setItem(selectedRow+1, 0, t1);
ui->tableExtensions->setItem(selectedRow+1, 1, t2);
ui->tableExtensions->selectRow(selectedRow+1);
}
QStringList FileExtensionManager::getDBFileExtensions() const
{
QStringList result;
for (int i = 0; i < ui->tableExtensions->rowCount(); ++i)
{
result.append(QString("%1 (%2)").arg(ui->tableExtensions->item(i, 0)->text(), ui->tableExtensions->item(i, 1)->text()));
}
return result;
}

View File

@ -0,0 +1,30 @@
#ifndef FILEEXTENSIONMANAGER_H
#define FILEEXTENSIONMANAGER_H
#include "WBFZExchangePluginAPI.h"
#include <QDialog>
namespace Ui {
class FileExtensionManager;
}
class FileExtensionManager : public QDialog
{
Q_OBJECT
public:
explicit FileExtensionManager(QStringList init, QWidget *parent = nullptr);
~FileExtensionManager() override;
QStringList getDBFileExtensions() const;
private:
Ui::FileExtensionManager *ui;
public slots:
void addItem();
void removeItem();
void upItem();
void downItem();
};
#endif // FILEEXTENSIONMANAGER_H

View File

@ -0,0 +1,153 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FileExtensionManager</class>
<widget class="QDialog" name="FileExtensionManager">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>578</width>
<height>463</height>
</rect>
</property>
<property name="windowTitle">
<string>File Extension Manager</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="buttonUp">
<property name="text">
<string>&amp;Up</string>
</property>
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/up</normaloff>:/icons/up</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonDown">
<property name="text">
<string>&amp;Down</string>
</property>
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/down</normaloff>:/icons/down</iconset>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="buttonAdd">
<property name="text">
<string>&amp;Add</string>
</property>
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/field_add</normaloff>:/icons/field_add</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonRemove">
<property name="text">
<string>&amp;Remove</string>
</property>
<property name="icon">
<iconset resource="icons/icons.qrc">
<normaloff>:/icons/field_delete</normaloff>:/icons/field_delete</iconset>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTableWidget" name="tableExtensions">
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderDefaultSectionSize">
<number>100</number>
</attribute>
<attribute name="verticalHeaderMinimumSectionSize">
<number>100</number>
</attribute>
<column>
<property name="text">
<string>Description</string>
</property>
</column>
<column>
<property name="text">
<string>Extensions</string>
</property>
</column>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="icons/icons.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>FileExtensionManager</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>FileExtensionManager</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,224 @@
#include "FilterLineEdit.h"
#include "Settings.h"
#include <QTimer>
#include <QKeyEvent>
#include <QMenu>
#include <QWhatsThis>
FilterLineEdit::FilterLineEdit(QWidget* parent, std::vector<FilterLineEdit*>* filters, size_t columnnum) :
QLineEdit(parent),
filterList(filters),
columnNumber(columnnum),
conditional_format(true)
{
setPlaceholderText(tr("Filter"));
setClearButtonEnabled(true);
setProperty("column", static_cast<int>(columnnum)); // Store the column number for later use
// Introduce a timer for delaying the signal triggered whenever the user changes the filter value.
// The idea here is that the textChanged() event isn't connected to the update filter slot directly anymore
// but instead there this timer mechanism in between: whenever the user changes the filter the delay timer
// is (re)started. As soon as the user stops typing the timer has a chance to trigger and call the
// delayedSignalTimerTriggered() method which then stops the timer and emits the delayed signal.
delaySignalTimer = new QTimer(this);
delaySignalTimer->setInterval(Settings::getValue("databrowser", "filter_delay").toInt()); // This is the milliseconds of not-typing we want to wait before triggering
connect(this, &FilterLineEdit::textChanged, delaySignalTimer, static_cast<void (QTimer::*)()>(&QTimer::start));
connect(delaySignalTimer, &QTimer::timeout, this, &FilterLineEdit::delayedSignalTimerTriggered);
setWhatsThis(tr("These input fields allow you to perform quick filters in the currently selected table.\n"
"By default, the rows containing the input text are filtered out.\n"
"The following operators are also supported:\n"
"%\tWildcard\n"
">\tGreater than\n"
"<\tLess than\n"
">=\tEqual to or greater\n"
"<=\tEqual to or less\n"
"=\tEqual to: exact match\n"
"<>\tUnequal: exact inverse match\n"
"x~y\tRange: values between x and y\n"
"/regexp/\tValues matching the regular expression"));
// Immediately emit the delayed filter value changed signal if the user presses the enter or the return key or
// the line edit widget loses focus
connect(this, &FilterLineEdit::editingFinished, this, &FilterLineEdit::delayedSignalTimerTriggered);
// Prepare for adding the What's This information and filter helper actions to the context menu
setContextMenuPolicy(Qt::CustomContextMenu);
connect(this, &FilterLineEdit::customContextMenuRequested, this, &FilterLineEdit::showContextMenu);
}
void FilterLineEdit::delayedSignalTimerTriggered()
{
// Stop the timer first to avoid triggering in intervals
delaySignalTimer->stop();
// Only emit text changed signal if the text has actually changed in comparison to the last emitted signal. This is necessary
// because this method is also called whenever the line edit loses focus and not only when its text has definitely been changed.
if(text() != lastValue)
{
// Emit the delayed signal using the current value
emit delayedTextChanged(text());
// Remember this value for the next time
lastValue = text();
}
}
void FilterLineEdit::keyReleaseEvent(QKeyEvent* event)
{
if(event->key() == Qt::Key_Tab)
{
if(filterList && columnNumber < filterList->size() - 1)
{
filterList->at(columnNumber + 1)->setFocus();
event->accept();
}
} else if(event->key() == Qt::Key_Backtab) {
if(filterList && columnNumber > 0)
{
filterList->at(columnNumber - 1)->setFocus();
event->accept();
}
}
}
void FilterLineEdit::clear()
{
// When programatically clearing the line edit's value make sure the effects are applied immediately, i.e.
// bypass the delayed signal timer
QLineEdit::clear();
delayedSignalTimerTriggered();
}
void FilterLineEdit::setText(const QString& text)
{
// When programatically setting the line edit's value make sure the effects are applied immediately, i.e.
// bypass the delayed signal timer
QLineEdit::setText(text);
delayedSignalTimerTriggered();
}
void FilterLineEdit::setFilterHelper(const QString& filterOperator, const QString& operatorSuffix)
{
setText(filterOperator + "?" + operatorSuffix);
// Select the value for easy editing of the expression
setSelection(filterOperator.length(), 1);
}
void FilterLineEdit::showContextMenu(const QPoint &pos)
{
// This has to be created here, otherwise the set of enabled options would not update accordingly.
QMenu* editContextMenu = createStandardContextMenu();
editContextMenu->addSeparator();
QMenu* filterMenu = editContextMenu->addMenu(tr("Set Filter Expression"));
QAction* whatsThisAction = new QAction(QIcon(":/icons/whatis"), tr("What's This?"), editContextMenu);
connect(whatsThisAction, &QAction::triggered, [&]() {
QWhatsThis::showText(pos, whatsThis(), this);
});
QAction* isNullAction = new QAction(tr("Is NULL"), editContextMenu);
connect(isNullAction, &QAction::triggered, [&]() {
setText("=NULL");
});
QAction* isNotNullAction = new QAction(tr("Is not NULL"), editContextMenu);
connect(isNotNullAction, &QAction::triggered, [&]() {
setText("<>NULL");
});
QAction* isEmptyAction = new QAction(tr("Is empty"), editContextMenu);
connect(isEmptyAction, &QAction::triggered, [&]() {
setText("=''");
});
QAction* isNotEmptyAction = new QAction(tr("Is not empty"), editContextMenu);
connect(isNotEmptyAction, &QAction::triggered, [&]() {
setText("<>''");
});
// Simplify this if we ever support a NOT LIKE filter
QAction* notContainingAction = new QAction(tr("Not containing..."), editContextMenu);
connect(notContainingAction, &QAction::triggered, [&]() {
setFilterHelper(QString ("/^((?!"), QString(").)*$/"));
});
QAction* equalToAction = new QAction(tr("Equal to..."), editContextMenu);
connect(equalToAction, &QAction::triggered, [&]() {
setFilterHelper(QString ("="));
});
QAction* notEqualToAction = new QAction(tr("Not equal to..."), editContextMenu);
connect(notEqualToAction, &QAction::triggered, [&]() {
setFilterHelper(QString ("<>"));
});
QAction* greaterThanAction = new QAction(tr("Greater than..."), editContextMenu);
connect(greaterThanAction, &QAction::triggered, [&]() {
setFilterHelper(QString (">"));
});
QAction* lessThanAction = new QAction(tr("Less than..."), editContextMenu);
connect(lessThanAction, &QAction::triggered, [&]() {
setFilterHelper(QString ("<"));
});
QAction* greaterEqualAction = new QAction(tr("Greater or equal..."), editContextMenu);
connect(greaterEqualAction, &QAction::triggered, [&]() {
setFilterHelper(QString (">="));
});
QAction* lessEqualAction = new QAction(tr("Less or equal..."), editContextMenu);
connect(lessEqualAction, &QAction::triggered, [&]() {
setFilterHelper(QString ("<="));
});
QAction* inRangeAction = new QAction(tr("In range..."), editContextMenu);
connect(inRangeAction, &QAction::triggered, [&]() {
setFilterHelper(QString ("?~"));
});
QAction* regexpAction = new QAction(tr("Regular expression..."), editContextMenu);
connect(regexpAction, &QAction::triggered, [&]() {
setFilterHelper(QString ("/"), QString ("/"));
});
if(conditional_format)
{
QAction* conditionalFormatAction;
if (text().isEmpty()) {
conditionalFormatAction = new QAction(QIcon(":/icons/clear_cond_formats"), tr("Clear All Conditional Formats"), editContextMenu);
connect(conditionalFormatAction, &QAction::triggered, [&]() {
emit clearAllCondFormats();
});
} else {
conditionalFormatAction = new QAction(QIcon(":/icons/add_cond_format"), tr("Use for Conditional Format"), editContextMenu);
connect(conditionalFormatAction, &QAction::triggered, [&]() {
emit addFilterAsCondFormat(text());
});
}
QAction* editCondFormatsAction = new QAction(QIcon(":/icons/edit_cond_formats"), tr("Edit Conditional Formats..."), editContextMenu);
connect(editCondFormatsAction, &QAction::triggered, [&]() {
emit editCondFormats();
});
editContextMenu->addSeparator();
editContextMenu->addAction(conditionalFormatAction);
editContextMenu->addAction(editCondFormatsAction);
}
filterMenu->addAction(whatsThisAction);
filterMenu->addSeparator();
filterMenu->addAction(isNullAction);
filterMenu->addAction(isNotNullAction);
filterMenu->addAction(isEmptyAction);
filterMenu->addAction(isNotEmptyAction);
filterMenu->addSeparator();
filterMenu->addAction(notContainingAction);
filterMenu->addAction(equalToAction);
filterMenu->addAction(notEqualToAction);
filterMenu->addAction(greaterThanAction);
filterMenu->addAction(lessThanAction);
filterMenu->addAction(greaterEqualAction);
filterMenu->addAction(lessEqualAction);
filterMenu->addAction(inRangeAction);
filterMenu->addAction(regexpAction);
editContextMenu->exec(mapToGlobal(pos));
}

View File

@ -0,0 +1,47 @@
#ifndef FILTERLINEEDIT_H
#define FILTERLINEEDIT_H
#include "WBFZExchangePluginAPI.h"
#include <QLineEdit>
#include <vector>
class QTimer;
class QKeyEvent;
class FilterLineEdit : public QLineEdit
{
Q_OBJECT
public:
explicit FilterLineEdit(QWidget* parent, std::vector<FilterLineEdit*>* filters = nullptr, size_t columnnum = 0);
// Override methods for programatically changing the value of the line edit
void clear();
void setText(const QString& text);
void setConditionFormatContextMenuEnabled(bool enable) { conditional_format = enable; }
private slots:
void delayedSignalTimerTriggered();
signals:
void delayedTextChanged(QString text);
void addFilterAsCondFormat(QString text);
void clearAllCondFormats();
void editCondFormats();
protected:
void keyReleaseEvent(QKeyEvent* event) override;
void setFilterHelper(const QString& filterOperator, const QString& operatorSuffix = QString());
private:
std::vector<FilterLineEdit*>* filterList;
size_t columnNumber;
QTimer* delaySignalTimer;
QString lastValue;
bool conditional_format;
private slots:
void showContextMenu(const QPoint &pos);
};
#endif

View File

@ -0,0 +1,130 @@
#include "FilterTableHeader.h"
#include "FilterLineEdit.h"
#include <QApplication>
#include <QTableView>
#include <QScrollBar>
FilterTableHeader::FilterTableHeader(QTableView* parent) :
QHeaderView(Qt::Horizontal, parent)
{
// Activate the click signals to allow sorting
setSectionsClickable(true);
// But use our own indicators allowing multi-column sorting
setSortIndicatorShown(false);
// Make sure to not automatically resize the columns according to the contents
setSectionResizeMode(QHeaderView::Interactive);
// Highlight column headers of selected cells to emulate spreadsheet behaviour
setHighlightSections(true);
// Do some connects: Basically just resize and reposition the input widgets whenever anything changes
connect(this, &FilterTableHeader::sectionResized, this, &FilterTableHeader::adjustPositions);
connect(parent->horizontalScrollBar(), &QScrollBar::valueChanged, this, &FilterTableHeader::adjustPositions);
connect(parent->verticalScrollBar(), &QScrollBar::valueChanged, this, &FilterTableHeader::adjustPositions);
// Set custom context menu handling
setContextMenuPolicy(Qt::CustomContextMenu);
}
void FilterTableHeader::generateFilters(size_t number, bool showFirst)
{
// Delete all the current filter widgets
qDeleteAll(filterWidgets);
filterWidgets.clear();
// And generate a bunch of new ones
for(size_t i=0;i < number; ++i)
{
FilterLineEdit* l = new FilterLineEdit(this, &filterWidgets, i);
if(!showFirst && i == 0) // This hides the first input widget which belongs to the hidden rowid column
l->setVisible(false);
else
l->setVisible(true);
connect(l, &FilterLineEdit::delayedTextChanged, this, &FilterTableHeader::inputChanged);
connect(l, &FilterLineEdit::addFilterAsCondFormat, this, &FilterTableHeader::addFilterAsCondFormat);
connect(l, &FilterLineEdit::clearAllCondFormats, this, &FilterTableHeader::clearAllCondFormats);
connect(l, &FilterLineEdit::editCondFormats, this, &FilterTableHeader::editCondFormats);
filterWidgets.push_back(l);
}
// Position them correctly
adjustPositions();
}
QSize FilterTableHeader::sizeHint() const
{
// For the size hint just take the value of the standard implementation and add the height of a input widget to it if necessary
QSize s = QHeaderView::sizeHint();
if(filterWidgets.size())
s.setHeight(s.height() + filterWidgets.at(0)->sizeHint().height() + 4); // The 4 adds just adds some extra space
return s;
}
void FilterTableHeader::updateGeometries()
{
// If there are any input widgets add a viewport margin to the header to generate some empty space for them which is not affected by scrolling
if(filterWidgets.size())
setViewportMargins(0, 0, 0, filterWidgets.at(0)->sizeHint().height());
else
setViewportMargins(0, 0, 0, 0);
// Now just call the parent implementation and reposition the input widgets
QHeaderView::updateGeometries();
adjustPositions();
}
void FilterTableHeader::adjustPositions()
{
// Loop through all widgets
for(int i=0;i < static_cast<int>(filterWidgets.size()); ++i)
{
// Get the current widget, move it and resize it
QWidget* w = filterWidgets.at(static_cast<size_t>(i));
// The two adds some extra space between the header label and the input widget
int y = QHeaderView::sizeHint().height() + 2;
if (QApplication::layoutDirection() == Qt::RightToLeft)
w->move(width() - (sectionPosition(i) + sectionSize(i) - offset()), y);
else
w->move(sectionPosition(i) - offset(), y);
w->resize(sectionSize(i), w->sizeHint().height());
}
}
void FilterTableHeader::inputChanged(const QString& new_value)
{
// Just get the column number and the new value and send them to anybody interested in filter changes
emit filterChanged(sender()->property("column").toUInt(), new_value);
}
void FilterTableHeader::addFilterAsCondFormat(const QString& filter)
{
// Just get the column number and the new value and send them to anybody interested in new conditional formatting
emit addCondFormat(sender()->property("column").toUInt(), filter);
}
void FilterTableHeader::clearAllCondFormats()
{
// Just get the column number and send it to anybody responsible or interested in clearing conditional formatting
emit allCondFormatsCleared(sender()->property("column").toUInt());
}
void FilterTableHeader::editCondFormats()
{
// Just get the column number and the new value and send them to anybody interested in editting conditional formatting
emit condFormatsEdited(sender()->property("column").toUInt());
}
void FilterTableHeader::clearFilters()
{
for(FilterLineEdit* filterLineEdit : filterWidgets)
filterLineEdit->clear();
}
void FilterTableHeader::setFilter(size_t column, const QString& value)
{
if(column < filterWidgets.size())
filterWidgets.at(column)->setText(value);
}

View File

@ -0,0 +1,44 @@
#ifndef FILTERTABLEHEADER_H
#define FILTERTABLEHEADER_H
#include "WBFZExchangePluginAPI.h"
#include <QHeaderView>
#include <vector>
class QTableView;
class FilterLineEdit;
class FilterTableHeader : public QHeaderView
{
Q_OBJECT
public:
explicit FilterTableHeader(QTableView* parent = nullptr);
QSize sizeHint() const override;
bool hasFilters() const {return (filterWidgets.size() > 0);}
public slots:
void generateFilters(size_t number, bool showFirst = false);
void adjustPositions();
void clearFilters();
void setFilter(size_t column, const QString& value);
signals:
void filterChanged(size_t column, QString value);
void addCondFormat(size_t column, QString filter);
void allCondFormatsCleared(size_t column);
void condFormatsEdited(size_t column);
protected:
void updateGeometries() override;
private slots:
void inputChanged(const QString& new_value);
void addFilterAsCondFormat(const QString& filter);
void clearAllCondFormats();
void editCondFormats();
private:
std::vector<FilterLineEdit*> filterWidgets;
};
#endif

View File

@ -0,0 +1,227 @@
#include "FindReplaceDialog.h"
#include "ui_FindReplaceDialog.h"
#include "ExtendedScintilla.h"
#include <QWhatsThis>
FindReplaceDialog::FindReplaceDialog(QWidget* parent)
: QDialog(parent),
ui(new Ui::FindReplaceDialog),
m_scintilla(nullptr),
foundIndicatorNumber(0),
findInProgress(false)
{
// Create UI
ui->setupUi(this);
}
FindReplaceDialog::~FindReplaceDialog()
{
delete ui;
}
void FindReplaceDialog::setExtendedScintilla(ExtendedScintilla* scintilla)
{
m_scintilla = scintilla;
// Create indicator for find-all and replace-all occurrences
foundIndicatorNumber = m_scintilla->indicatorDefine(QsciScintilla::StraightBoxIndicator);
m_scintilla->setIndicatorForegroundColor(Qt::magenta, foundIndicatorNumber);
m_scintilla->setIndicatorDrawUnder(true, foundIndicatorNumber);
bool isWriteable = ! m_scintilla->isReadOnly();
ui->replaceWithText->setEnabled(isWriteable);
ui->replaceButton->setEnabled(isWriteable);
ui->replaceAllButton->setEnabled(isWriteable);
connect(m_scintilla, &ExtendedScintilla::destroyed, this, &FindReplaceDialog::hide);
connect(ui->findText, &QLineEdit::editingFinished, this, &FindReplaceDialog::cancelFind);
connect(ui->regexpCheckBox, &QCheckBox::toggled, this, &FindReplaceDialog::cancelFind);
connect(ui->caseCheckBox, &QCheckBox::toggled, this, &FindReplaceDialog::cancelFind);
connect(ui->wholeWordsCheckBox, &QCheckBox::toggled, this, &FindReplaceDialog::cancelFind);
connect(ui->wrapCheckBox, &QCheckBox::toggled, this, &FindReplaceDialog::cancelFind);
connect(ui->backwardsCheckBox, &QCheckBox::toggled, this, &FindReplaceDialog::cancelFind);
connect(ui->selectionCheckBox, &QCheckBox::toggled, this, &FindReplaceDialog::cancelFind);
connect(ui->selectionCheckBox, &QCheckBox::toggled, ui->wrapCheckBox, &QCheckBox::setDisabled);
}
bool FindReplaceDialog::findFirst(bool wrap, bool forward)
{
if (ui->selectionCheckBox->isChecked())
return m_scintilla->findFirstInSelection
(ui->findText->text(),
ui->regexpCheckBox->isChecked(),
ui->caseCheckBox->isChecked(),
ui->wholeWordsCheckBox->isChecked(),
forward,
/* show */ true,
/* posix */ true,
/* cxx11 */ true);
else
return m_scintilla->findFirst
(ui->findText->text(),
ui->regexpCheckBox->isChecked(),
ui->caseCheckBox->isChecked(),
ui->wholeWordsCheckBox->isChecked(),
wrap,
forward,
/* line */ -1,
/* index */ -1,
/* show */ true,
/* posix */ true,
/* cxx11 */ true);
}
bool FindReplaceDialog::findNext()
{
clearIndicators();
if (findInProgress)
findInProgress = m_scintilla->findNext();
else
findInProgress = findFirst(ui->wrapCheckBox->isChecked(),
!ui->backwardsCheckBox->isChecked());
if (!findInProgress)
ui->messageLabel->setText(tr("The searched text was not found"));
return findInProgress;
}
void FindReplaceDialog::showFindReplaceDialog(bool hasReplace)
{
ui->replaceWithText->setVisible(hasReplace);
ui->replaceButton->setVisible(hasReplace);
ui->replaceWithLabel->setVisible(hasReplace);
ui->replaceAllButton->setVisible(hasReplace);
show();
}
void FindReplaceDialog::show()
{
ui->findText->setFocus();
// If there is multi-line selected text set automatically the selection
// check box. If it's only part of a line, use it as text to find.
if (m_scintilla->hasSelectedText())
if (m_scintilla->selectedText().contains("\n"))
ui->selectionCheckBox->setChecked(true);
else {
ui->findText->setText(m_scintilla->selectedText());
ui->selectionCheckBox->setChecked(false);
}
else
ui->selectionCheckBox->setChecked(false);
ui->findText->selectAll();
QDialog::show();
}
void FindReplaceDialog::replace()
{
if (m_scintilla->hasSelectedText())
m_scintilla->replace(ui->replaceWithText->text());
findNext();
}
void FindReplaceDialog::indicateSelection()
{
int fromRow, fromIndex, toRow, toIndex;
m_scintilla->getSelection(&fromRow, &fromIndex, &toRow, &toIndex);
m_scintilla->fillIndicatorRange(fromRow, fromIndex, toRow, toIndex, foundIndicatorNumber);
}
void FindReplaceDialog::searchAll(bool replace)
{
clearIndicators();
if (!ui->selectionCheckBox->isChecked())
m_scintilla->setCursorPosition(0, 0);
bool found = findFirst(/* wrap */ false, /* fordward */ true);
int occurrences = 0;
while (found) {
if (replace)
m_scintilla->replace(ui->replaceWithText->text());
indicateSelection();
++occurrences;
found = m_scintilla->findNext();
}
if (!ui->selectionCheckBox->isChecked())
m_scintilla->clearSelection();
QString message;
switch (occurrences) {
case 0:
message = tr("The searched text was not found.");
break;
case 1:
if (replace)
message = tr("The searched text was replaced one time.");
else
message = tr("The searched text was found one time.");
break;
default:
if (replace)
message = tr("The searched text was replaced %1 times.").arg(occurrences);
else
message = tr("The searched text was found %1 times.").arg(occurrences);
break;
}
ui->messageLabel->setText(message);
}
void FindReplaceDialog::findAll()
{
searchAll(/* replace */ false);
}
void FindReplaceDialog::replaceAll()
{
searchAll(/* replace */ true);
}
void FindReplaceDialog::cancelFind()
{
m_scintilla->findFirst(QString(), false, false, false, false);
clearIndicators();
findInProgress = false;
ui->messageLabel->setText("");
}
void FindReplaceDialog::help()
{
QWhatsThis::enterWhatsThisMode();
}
void FindReplaceDialog::clearIndicators()
{
m_scintilla->clearIndicatorRange(0, 0, m_scintilla->lines(), m_scintilla->lineLength(m_scintilla->lines()), foundIndicatorNumber);
ui->messageLabel->setText("");
}
void FindReplaceDialog::close()
{
m_scintilla->clearSelection();
// Reset any previous find so it does not interfere with the next time
// the dialog is open.
cancelFind();
QDialog::close();
}
void FindReplaceDialog::buttonBox_clicked(QAbstractButton* button)
{
if (button == ui->buttonBox->button(QDialogButtonBox::Help))
help();
else if (button == ui->buttonBox->button(QDialogButtonBox::Close))
close();
}
void FindReplaceDialog::reject()
{
close();
QDialog::reject();
}

View File

@ -0,0 +1,48 @@
#ifndef FindReplaceDialog_H
#define FindReplaceDialog_H
#include "WBFZExchangePluginAPI.h"
#include <QDialog>
class QAbstractButton;
class ExtendedScintilla;
namespace Ui {
class FindReplaceDialog;
}
class FindReplaceDialog : public QDialog
{
Q_OBJECT
public:
explicit FindReplaceDialog(QWidget* parent = nullptr);
~FindReplaceDialog() override;
void setExtendedScintilla(ExtendedScintilla* scintilla);
void show();
void showFindReplaceDialog(bool hasReplace);
private slots:
bool findNext();
void replace();
void findAll();
void replaceAll();
void cancelFind();
void help();
void close();
void reject() override;
void buttonBox_clicked(QAbstractButton* button);
private:
bool findFirst(bool wrap, bool forward);
void searchAll(bool replace);
void indicateSelection();
void clearIndicators();
Ui::FindReplaceDialog* ui;
ExtendedScintilla* m_scintilla;
int foundIndicatorNumber;
bool findInProgress;
};
#endif

View File

@ -0,0 +1,322 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FindReplaceDialog</class>
<widget class="QDialog" name="FindReplaceDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>451</width>
<height>288</height>
</rect>
</property>
<property name="windowTitle">
<string>Find and Replace</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QWidget" name="replaceBlock" native="true">
<property name="enabled">
<bool>true</bool>
</property>
<layout class="QHBoxLayout" name="filePickerLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::ExpandingFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="findTextLabel">
<property name="text">
<string>Fi&amp;nd text:</string>
</property>
<property name="buddy">
<cstring>findText</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="findText"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="replaceWithLabel">
<property name="text">
<string>Re&amp;place with:</string>
</property>
<property name="buddy">
<cstring>replaceWithText</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="replaceWithText"/>
</item>
<item row="4" column="1">
<widget class="QCheckBox" name="caseCheckBox">
<property name="text">
<string>Match &amp;exact case</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QCheckBox" name="wholeWordsCheckBox">
<property name="text">
<string>Match &amp;only whole words</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QCheckBox" name="wrapCheckBox">
<property name="whatsThis">
<string>When enabled, the search continues from the other end when it reaches one end of the page</string>
</property>
<property name="text">
<string>&amp;Wrap around</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QCheckBox" name="backwardsCheckBox">
<property name="whatsThis">
<string>When set, the search goes backwards from cursor position, otherwise it goes forward</string>
</property>
<property name="text">
<string>Search &amp;backwards</string>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QCheckBox" name="selectionCheckBox">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When checked, the pattern to find is searched only in the current selection.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>&amp;Selection only</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QCheckBox" name="regexpCheckBox">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When checked, the pattern to find is interpreted as a UNIX regular expression. See &lt;a href=&quot;https://en.wikibooks.org/wiki/Regular_Expressions&quot;&gt;Regular Expression in Wikibooks&lt;/a&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Use regular e&amp;xpressions</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>7</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="findNextButton">
<property name="whatsThis">
<string>Find the next occurrence from the cursor position and in the direction set by &quot;Search backwards&quot;</string>
</property>
<property name="text">
<string>&amp;Find Next</string>
</property>
<property name="shortcut">
<string>F3</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="replaceButton">
<property name="text">
<string>&amp;Replace</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="findAllButton">
<property name="whatsThis">
<string>Highlight all the occurrences of the text in the page</string>
</property>
<property name="text">
<string>F&amp;ind All</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="replaceAllButton">
<property name="whatsThis">
<string>Replace all the occurrences of the text in the page</string>
</property>
<property name="text">
<string>Replace &amp;All</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QLabel" name="messageLabel">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Close|QDialogButtonBox::Help</set>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>findText</tabstop>
<tabstop>replaceWithText</tabstop>
<tabstop>caseCheckBox</tabstop>
<tabstop>wholeWordsCheckBox</tabstop>
<tabstop>wrapCheckBox</tabstop>
<tabstop>backwardsCheckBox</tabstop>
<tabstop>selectionCheckBox</tabstop>
<tabstop>regexpCheckBox</tabstop>
<tabstop>findNextButton</tabstop>
<tabstop>replaceButton</tabstop>
<tabstop>findAllButton</tabstop>
<tabstop>replaceAllButton</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>findNextButton</sender>
<signal>clicked()</signal>
<receiver>FindReplaceDialog</receiver>
<slot>findNext()</slot>
<hints>
<hint type="sourcelabel">
<x>225</x>
<y>242</y>
</hint>
<hint type="destinationlabel">
<x>225</x>
<y>132</y>
</hint>
</hints>
</connection>
<connection>
<sender>replaceButton</sender>
<signal>clicked()</signal>
<receiver>FindReplaceDialog</receiver>
<slot>replace()</slot>
<hints>
<hint type="sourcelabel">
<x>225</x>
<y>242</y>
</hint>
<hint type="destinationlabel">
<x>225</x>
<y>132</y>
</hint>
</hints>
</connection>
<connection>
<sender>findAllButton</sender>
<signal>clicked()</signal>
<receiver>FindReplaceDialog</receiver>
<slot>findAll()</slot>
<hints>
<hint type="sourcelabel">
<x>225</x>
<y>242</y>
</hint>
<hint type="destinationlabel">
<x>225</x>
<y>132</y>
</hint>
</hints>
</connection>
<connection>
<sender>replaceAllButton</sender>
<signal>clicked()</signal>
<receiver>FindReplaceDialog</receiver>
<slot>replaceAll()</slot>
<hints>
<hint type="sourcelabel">
<x>225</x>
<y>242</y>
</hint>
<hint type="destinationlabel">
<x>225</x>
<y>132</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>clicked(QAbstractButton*)</signal>
<receiver>FindReplaceDialog</receiver>
<slot>buttonBox_clicked(QAbstractButton*)</slot>
<hints>
<hint type="sourcelabel">
<x>225</x>
<y>265</y>
</hint>
<hint type="destinationlabel">
<x>225</x>
<y>143</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>updatePreview()</slot>
<slot>checkInput()</slot>
<slot>updateSelection(bool)</slot>
<slot>matchSimilar()</slot>
</slots>
</ui>

View File

@ -0,0 +1,191 @@
#include "WBFZExchangePluginAPI.h"
#include "sqlitedb.h"
#include "ForeignKeyEditorDelegate.h"
#include <QComboBox>
#include <QLineEdit>
#include <QPushButton>
#include <QHBoxLayout>
class ForeignKeyEditor : public QWidget
{
Q_OBJECT
public:
explicit ForeignKeyEditor(QWidget* parent = nullptr)
: QWidget(parent)
, tablesComboBox(new QComboBox(this))
, idsComboBox(new QComboBox(this))
, clauseEdit(new QLineEdit(this))
, m_btnReset(new QPushButton(tr("&Reset"), this))
{
idsComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents);
clauseEdit->setPlaceholderText(tr("Foreign key clauses (ON UPDATE, ON DELETE etc.)"));
QHBoxLayout* layout = new QHBoxLayout(this);
layout->addWidget(tablesComboBox);
layout->addWidget(idsComboBox);
layout->addWidget(clauseEdit);
layout->addWidget(m_btnReset);
layout->setSpacing(0);
layout->setMargin(0);
setLayout(layout);
connect(m_btnReset, &QPushButton::clicked, [&]
{
tablesComboBox->setCurrentIndex(-1);
idsComboBox->setCurrentIndex(-1);
clauseEdit->clear();
});
connect(tablesComboBox, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
[=](int index)
{
// reset ids combo box
idsComboBox->setCurrentIndex(-1);
// disable clauses editor if none of tables is selected
bool enableClausesEditor = (index!= -1);
clauseEdit->setEnabled(enableClausesEditor);
});
}
QString getSql() const
{
if (tablesComboBox->currentText().isEmpty())
return QString();
const QString table = sqlb::escapeIdentifier(tablesComboBox->currentText());
const QString clauses = clauseEdit->text();
QString id = idsComboBox->currentText();
if (!id.isEmpty())
id = QString("(%1)").arg(sqlb::escapeIdentifier(id));
return QString("%1%2 %3").arg(
table,
id,
clauses)
.trimmed();
}
QComboBox* tablesComboBox;
QComboBox* idsComboBox;
QLineEdit* clauseEdit; // for ON CASCADE and such
private:
QPushButton* m_btnReset;
};
ForeignKeyEditorDelegate::ForeignKeyEditorDelegate(const DBBrowserDB& db, sqlb::Table& table, QObject* parent)
: QStyledItemDelegate(parent)
, m_db(db)
, m_table(table)
{
for(const auto& it : m_db.schemata)
{
for(const auto& jt : it.second)
{
// Don't insert the current table into the list. The name and fields of the current table are always taken from the m_table reference
if(jt.second->type() == sqlb::Object::Types::Table && jt.second->name() != m_table.name())
m_tablesIds.insert({jt.second->name(), std::dynamic_pointer_cast<sqlb::Table>(jt.second)->fieldNames()});
}
}
}
QWidget* ForeignKeyEditorDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
Q_UNUSED(option)
Q_UNUSED(index)
ForeignKeyEditor* editor = new ForeignKeyEditor(parent);
editor->setAutoFillBackground(true);
connect(editor->tablesComboBox, static_cast<void(QComboBox::*)(const QString&)>(&QComboBox::currentIndexChanged),
[=](const QString& tableName)
{
QComboBox* box = editor->idsComboBox;
box->clear();
box->addItem(QString()); // for those heroes who don't like to specify key explicitly
// For recursive foreign keys get the field list from the m_table reference. For other foreign keys from the prepared field lists.
if(tableName.toStdString() == m_table.name())
{
for(const auto& n : m_table.fieldNames())
box->addItem(QString::fromStdString(n));
} else {
for(const auto& n : m_tablesIds[tableName.toStdString()])
box->addItem(QString::fromStdString(n));
}
box->setCurrentIndex(0);
});
editor->tablesComboBox->clear();
for(const auto& i : m_tablesIds)
editor->tablesComboBox->addItem(QString::fromStdString(i.first));
editor->tablesComboBox->addItem(QString::fromStdString(m_table.name())); // For recursive foreign keys
return editor;
}
void ForeignKeyEditorDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const
{
ForeignKeyEditor* fkEditor = static_cast<ForeignKeyEditor*>(editor);
size_t column = static_cast<size_t>(index.row()); // weird? I know right
const sqlb::Field& field = m_table.fields.at(column);
auto fk = std::dynamic_pointer_cast<sqlb::ForeignKeyClause>(m_table.constraint({field.name()}, sqlb::Constraint::ForeignKeyConstraintType));
if (fk) {
fkEditor->tablesComboBox->setCurrentText(QString::fromStdString(fk->table()));
fkEditor->clauseEdit->setText(QString::fromStdString(fk->constraint()));
if (!fk->columns().empty())
fkEditor->idsComboBox->setCurrentText(QString::fromStdString(fk->columns().front()));
} else {
fkEditor->tablesComboBox->setCurrentIndex(-1);
}
}
void ForeignKeyEditorDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const
{
ForeignKeyEditor* fkEditor = static_cast<ForeignKeyEditor*>(editor);
QString sql = fkEditor->getSql();
size_t column = static_cast<size_t>(index.row());
const sqlb::Field& field = m_table.fields.at(column);
if (sql.isEmpty()) {
// Remove the foreign key
m_table.removeConstraints({field.name()}, sqlb::Constraint::ConstraintTypes::ForeignKeyConstraintType);
} else {
// Set the foreign key
sqlb::ForeignKeyClause* fk = new sqlb::ForeignKeyClause;
const QString table = fkEditor->tablesComboBox->currentText();
const std::string id = fkEditor->idsComboBox->currentText().toStdString();
const QString clause = fkEditor->clauseEdit->text();
fk->setTable(table.toStdString());
fk->setColumnList({ field.name() });
if (!id.empty())
fk->setColumns({id});
if (!clause.trimmed().isEmpty()) {
fk->setConstraint(clause.toStdString());
}
m_table.setConstraint(sqlb::ConstraintPtr(fk));
}
model->setData(index, sql);
}
void ForeignKeyEditorDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
Q_UNUSED(index)
editor->setGeometry(option.rect);
}
#include "ForeignKeyEditorDelegate.moc"

View File

@ -0,0 +1,34 @@
#ifndef FOREIGNKEYDELEGATE_H
#define FOREIGNKEYDELEGATE_H
#include "WBFZExchangePluginAPI.h"
#include <QStyledItemDelegate>
#include <unordered_map>
#include <vector>
#include <string>
class DBBrowserDB;
namespace sqlb
{
class Table;
}
class ForeignKeyEditorDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
explicit ForeignKeyEditorDelegate(const DBBrowserDB& db, sqlb::Table& table, QObject* parent = nullptr);
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
void setEditorData(QWidget* editor, const QModelIndex& index) const override;
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
private:
const DBBrowserDB& m_db;
sqlb::Table& m_table;
mutable std::unordered_map<std::string, std::vector<std::string>> m_tablesIds;
};
#endif // FOREIGNKEYDELEGATE_H

View File

@ -0,0 +1,22 @@
#include "IconCache.h"
QIcon IconCache::null_icon;
std::unordered_map<std::string, QIcon> IconCache::icons;
const QIcon& IconCache::get(const std::string& name)
{
// Check if we already have an icon object with that name in the cache. If so, just return that
auto it = icons.find(name);
if(it != icons.end())
return it->second;
// If now, try to load an icon with that name and insert it into the cache
QIcon icon(":/icons/" + QString::fromStdString(name));
auto ins = icons.insert({name, icon});
// If the insertion was successful, return the inserted icon object. If it was not, return a null icon.
if(ins.second)
return ins.first->second;
else
return null_icon;
}

Some files were not shown because too many files have changed in this diff Show More