完成微波仿真、点云构网、模型导出、仿真成像功能移植
parent
a6b5ce7003
commit
51341c8e72
|
@ -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>
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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>
|
|
@ -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"撤销");//添加QAction,Ctrl-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"重做");//添加QAction,Ctrl-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;
|
||||
}
|
|
@ -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
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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>>></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><<</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>
|
|
@ -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)) //可以用QIODevice,Truncate表示清空原来的内容
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
|
@ -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
|
@ -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
|
|
@ -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::min,pcl::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 |
|
@ -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 |
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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><html><head/><body><p>DB Browser for SQLite is an open source, freeware visual tool used to create, design and edit SQLite database files.</p><p>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.</p><p>See <a href="http://www.gnu.org/licenses/gpl.html">http://www.gnu.org/licenses/gpl.html</a> and <a href="https://www.mozilla.org/MPL/2.0/index.txt">https://www.mozilla.org/MPL/2.0/index.txt</a> for details.</p><p>For more information on this program please visit our website at: <a href="http://sqlitebrowser.org">http://sqlitebrowser.org</a></p><p><span style=" font-size:small;">This software uses the GPL/LGPL Qt Toolkit from </span><a href="http://qt-project.org/"><span style=" font-size:small;">http://qt-project.org/</span></a><span style=" font-size:small;"><br/>See </span><a href="http://qt-project.org/doc/qt-5/licensing.html"><span style=" font-size:small;">http://qt-project.org/doc/qt-5/licensing.html</span></a><span style=" font-size:small;"> for licensing terms and information.</span></p><p><span style=" font-size:small;">It also uses the Silk icon set by Mark James licensed under a Creative Commons Attribution 2.5 and 3.0 license.<br/>See </span><a href="http://www.famfamfam.com/lab/icons/silk/"><span style=" font-size:small;">http://www.famfamfam.com/lab/icons/silk/</span></a><span style=" font-size:small;"> for details.</span></p></body></html></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>
|
|
@ -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"
|
|
@ -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
|
|
@ -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><html><head/><body><p><span style=" font-weight:600;">Save</span> will submit the shown SQL statement to the database for inserting the new record.</p><p><span style=" font-weight:600;">Restore Defaults</span> will restore the initial values in the <span style=" font-weight:600;">Value</span> column.</p><p><span style=" font-weight:600;">Cancel</span> will close this dialog without executing the query.</p></body></html></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>
|
|
@ -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));
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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>&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>&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&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 &3 defaults</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioEncryptionSqlCipher4">
|
||||
<property name="text">
|
||||
<string>SQLCipher &4 defaults</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioEncryptionCustom">
|
||||
<property name="text">
|
||||
<string>Custo&m</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Page si&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>&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>
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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"));
|
||||
}
|
|
@ -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
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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>&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>&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 &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 &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 |
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
@ -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
|
|
@ -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>&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>&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 &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>&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>
|
|
@ -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));
|
||||
}
|
|
@ -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
|
|
@ -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>&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>&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>&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&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&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
|
@ -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
|
|
@ -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&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><html><head/><body><p><span style=" font-weight:600; color:#ff0000;">Warning: </span>There is something with this table definition that our parser doesn't fully understand. Modifying and saving this table might result in problems.</p></body></html></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>
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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&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&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&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>&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>"</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>
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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&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>&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>
|
|
@ -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);
|
||||
}
|
|
@ -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
|
@ -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 ¤t, 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 ¤t, const QModelIndex &previous) override;
|
||||
|
||||
FilterTableHeader* m_tableHeader;
|
||||
QMenu* m_contextMenu;
|
||||
ExtendedTableWidgetEditorDelegate* m_editorDelegate;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -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
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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>&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>&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>&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>&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>
|
|
@ -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));
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -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&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&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 &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 &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>&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 &backwards</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<widget class="QCheckBox" name="selectionCheckBox">
|
||||
<property name="whatsThis">
|
||||
<string><html><head/><body><p>When checked, the pattern to find is searched only in the current selection.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Selection only</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1">
|
||||
<widget class="QCheckBox" name="regexpCheckBox">
|
||||
<property name="whatsThis">
|
||||
<string><html><head/><body><p>When checked, the pattern to find is interpreted as a UNIX regular expression. See <a href="https://en.wikibooks.org/wiki/Regular_Expressions">Regular Expression in Wikibooks</a>.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use regular e&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 "Search backwards"</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Find Next</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>F3</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="replaceButton">
|
||||
<property name="text">
|
||||
<string>&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&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 &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>
|
|
@ -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"
|
|
@ -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
|
|
@ -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
Loading…
Reference in New Issue