PIValueTreeEdit cut/copy/paste support

EMainWindow saveAs incorrect filename to recent fix
QAD::loadTranslations now also look into standard translation location
This commit is contained in:
2023-04-28 15:33:50 +03:00
parent 4eefbe4e46
commit 76a1b35f14
9 changed files with 289 additions and 261 deletions

View File

@@ -4,7 +4,7 @@ cmake_policy(SET CMP0072 NEW) # FindOpenGL prefers GLVND by default
project(QAD)
set(QAD_MAJOR 2)
set(QAD_MINOR 16)
set(QAD_REVISION 2)
set(QAD_REVISION 3)
set(QAD_SUFFIX )
set(QAD_COMPANY SHS)
set(QAD_DOMAIN org.SHS)

View File

@@ -416,7 +416,7 @@ bool EMainWindow::saveFile(bool ask) {
bool EMainWindow::saveAsFile() {
QString ret = QFileDialog::getSaveFileName(this, tr("Select file to save"), file_name, saveFilter());
if (ret.isEmpty()) return false;
if (save(ret)) addToRecent(ret);
if (save(ret)) addToRecent(file_name);
return true;
}

View File

@@ -1,11 +1,14 @@
#include "pivaluetree_edit.h"
#include "pijson.h"
#include "piqt.h"
#include "pivaluetree_conversions.h"
#include "pivaluetree_edit_parameters.h"
#include "pivaluetree_edit_reorder.h"
#include "pivariant_edit.h"
#include "ui_pivaluetree_edit_array.h"
#include <QClipboard>
#include <QEvent>
#include <QFormLayout>
#include <QGroupBox>
@@ -19,6 +22,20 @@ using Attribute = PIValueTree::Attribute;
const char property_name[] = "__name__";
class ToolButton: public QToolButton {
public:
ToolButton(QWidget * parent = nullptr): QToolButton(parent) {}
void setPressHandler(std::function<void()> f) { press_handler = f; }
protected:
void mousePressEvent(QMouseEvent * e) override {
if (press_handler) press_handler();
QToolButton::mousePressEvent(e);
}
std::function<void()> press_handler;
};
class GroupBox: public QGroupBox {
public:
GroupBox(QWidget * content = nullptr): QGroupBox() {
@@ -239,134 +256,210 @@ void PIValueTreeEdit::applyValues() const {
}
void PIValueTreeEdit::actionRename(QToolButton * button, const PIString & vn) {
PIString nn = Q2PIString(QInputDialog::getText(nullptr, tr("Rename"), tr("Input new name:"), QLineEdit::Normal, PI2QString(vn)));
if (nn.isEmpty() || (nn == vn)) return;
for (const auto & c: current.children()) {
if (c.name() == nn) {
QMessageBox::critical(nullptr, tr("Rename"), tr("This name already exists!"));
return;
}
}
current[vn].setName(nn);
button->setProperty(property_name, PI2QString(nn));
grid->rename(vn, nn);
if (value_edits.contains(vn)) {
value_edits[nn] = value_edits[vn];
value_edits.remove(vn);
}
if (tree_edits.contains(vn)) {
tree_edits[nn] = tree_edits[vn];
tree_edits.remove(vn);
}
if (comm_labels.contains(vn)) {
comm_labels[nn] = comm_labels[vn];
comm_labels.remove(vn);
}
if (label_labels.contains(vn)) {
label_labels[nn] = label_labels[vn];
label_labels[nn]->setText(PIVariantEditorBase::vtTr(nn));
label_labels.remove(vn);
}
if (tab_widget) {
for (int i = 0; i < tab_widget->count(); ++i) {
if (tab_widget->tabBar()->tabData(i).toString() == PI2QString(vn)) {
tab_widget->setTabText(i, PIVariantEditorBase::vtTr(nn));
tab_widget->tabBar()->setTabData(i, PI2QString(nn));
break;
}
}
}
}
void PIValueTreeEdit::actionRemove(QToolButton * button, const PIString & vn) {
current.remove(vn);
if (tab_widget) {
QString qvn = PI2QString(vn);
QMetaObject::invokeMethod(
this,
[this, qvn]() {
for (int i = 0; i < tab_widget->count(); ++i) {
if (tab_widget->tabBar()->tabData(i).toString() == qvn) {
tab_widget->removeTab(i);
break;
}
}
if (tab_widget->count() == 0) {
grid->removeRow(grid->getRow(tab_widget));
tab_widget = nullptr;
}
},
Qt::QueuedConnection);
}
grid->removeRow(grid->getRow(button));
value_edits.remove(vn);
tree_edits.remove(vn);
comm_labels.remove(vn);
label_labels.remove(vn);
}
void PIValueTreeEdit::actionChange(QToolButton * button, const PIString & vn) {
auto & vt(current[vn]);
if (vt.isArray()) {
auto * ve = tree_edits.value(vn, nullptr);
if (!ve) return;
vt = ve->value();
if (!widget_params->showFor(vt)) return;
ve->setValue(vt);
ve->resizeArray();
// ve->applyArrayAttributes();
} else {
bool was_label = vt.attribute(Attribute::isLabel, false).toBool();
auto * ve = value_edits.value(vn, nullptr);
if (ve) {
vt.setValue(ve->value());
vt.mergeAttributes(ve->attributes());
}
if (!widget_params->showFor(vt)) return;
bool now_label = vt.attribute(Attribute::isLabel, false).toBool();
if (was_label ^ now_label) {
if (now_label) {
auto * l = newLabel(vt);
grid->replace(grid->getRow(button), l);
value_edits.remove(vt.name());
comm_labels.remove(vt.name());
} else {
auto * ve = new PIVariantEdit();
applyVariantEdit(ve, vt);
grid->replace(grid->getRow(button), vt.name(), ve, vt.comment());
value_edits[vt.name()] = ve;
}
ve = nullptr;
}
if (ve) {
applyVariantEdit(ve, vt);
}
if (now_label) {
label_labels[vt.name()]->setStyleSheet(PI2QString(vt.attribute(Attribute::style).toString()));
}
}
auto * cl = comm_labels.value(vn, nullptr);
if (cl) cl->setText(PIVariantEditorBase::vtTr(vt.comment()));
}
void PIValueTreeEdit::actionReorder(QToolButton *, const PIString & vn) {
if (!widget_reorder->showFor(current)) return;
grid->reorder(widget_reorder->map);
auto cl = current.children();
current.clearChildren();
for (int i = 0; i < cl.size_s(); ++i) {
int mi = widget_reorder->map.value(i, i);
if (mi < 0 || mi >= cl.size_s()) continue;
current.addChild(cl[mi]);
}
}
void PIValueTreeEdit::actionCopy(QToolButton *, const PIString & vn) {
PIValueTree cur_val = value();
auto & vt(cur_val[vn]);
// piCout << vt;
PIString json =
PIValueTreeConversions::toJSON(vt, PIValueTreeConversions::Default | PIValueTreeConversions::IncludeRoot).toJSON(PIJSON::Tree);
QApplication::clipboard()->setText(PI2QString(json));
}
void PIValueTreeEdit::actionPaste(QToolButton * button, const PIString & vn, int offset) {
PIString json = Q2PIString(QApplication::clipboard()->text());
if (json.isEmpty()) return;
PIValueTree ins_val = PIValueTreeConversions::fromJSON(PIJSON::fromJSON(json));
// if (ins_val.name.isEmpty()) return;
PIValueTree cur_val = value();
cur_val.insertChild(cur_val.childIndex(vn) + offset, ins_val);
setValue(cur_val);
}
void PIValueTreeEdit::actionTriggered(QToolButton * button, const PIString & vn, QAction * a) {
if (a == widget_params->actionRename) {
PIString nn = Q2PIString(QInputDialog::getText(nullptr, tr("Rename"), tr("Input new name:"), QLineEdit::Normal, PI2QString(vn)));
if (nn.isEmpty() || (nn == vn)) return;
for (const auto & c: current.children()) {
if (c.name() == nn) {
QMessageBox::critical(nullptr, tr("Rename"), tr("This name already exists!"));
return;
}
}
current[vn].setName(nn);
button->setProperty(property_name, PI2QString(nn));
grid->rename(vn, nn);
if (value_edits.contains(vn)) {
value_edits[nn] = value_edits[vn];
value_edits.remove(vn);
}
if (tree_edits.contains(vn)) {
tree_edits[nn] = tree_edits[vn];
tree_edits.remove(vn);
}
if (comm_labels.contains(vn)) {
comm_labels[nn] = comm_labels[vn];
comm_labels.remove(vn);
}
if (label_labels.contains(vn)) {
label_labels[nn] = label_labels[vn];
label_labels[nn]->setText(PIVariantEditorBase::vtTr(nn));
label_labels.remove(vn);
}
if (tab_widget) {
for (int i = 0; i < tab_widget->count(); ++i) {
if (tab_widget->tabBar()->tabData(i).toString() == PI2QString(vn)) {
tab_widget->setTabText(i, PIVariantEditorBase::vtTr(nn));
tab_widget->tabBar()->setTabData(i, PI2QString(nn));
break;
}
}
}
actionRename(button, vn);
return;
}
if (a == widget_params->actionRemove) {
current.remove(vn);
if (tab_widget) {
QString qvn = PI2QString(vn);
QMetaObject::invokeMethod(
this,
[this, qvn]() {
for (int i = 0; i < tab_widget->count(); ++i) {
if (tab_widget->tabBar()->tabData(i).toString() == qvn) {
tab_widget->removeTab(i);
break;
}
}
if (tab_widget->count() == 0) {
grid->removeRow(grid->getRow(tab_widget));
tab_widget = nullptr;
}
},
Qt::QueuedConnection);
}
grid->removeRow(grid->getRow(button));
value_edits.remove(vn);
tree_edits.remove(vn);
comm_labels.remove(vn);
label_labels.remove(vn);
actionRemove(button, vn);
return;
}
if (a == widget_params->actionChange) {
auto & vt(current[vn]);
if (vt.isArray()) {
auto * ve = tree_edits.value(vn, nullptr);
if (!ve) return;
vt = ve->value();
if (!widget_params->showFor(vt)) return;
ve->setValue(vt);
ve->resizeArray();
// ve->applyArrayAttributes();
} else {
bool was_label = vt.attribute(Attribute::isLabel, false).toBool();
auto * ve = value_edits.value(vn, nullptr);
if (ve) {
vt.setValue(ve->value());
vt.mergeAttributes(ve->attributes());
}
if (!widget_params->showFor(vt)) return;
bool now_label = vt.attribute(Attribute::isLabel, false).toBool();
if (was_label ^ now_label) {
if (now_label) {
auto * l = newLabel(vt);
grid->replace(grid->getRow(button), l);
value_edits.remove(vt.name());
comm_labels.remove(vt.name());
} else {
auto * ve = new PIVariantEdit();
applyVariantEdit(ve, vt);
grid->replace(grid->getRow(button), vt.name(), ve, vt.comment());
value_edits[vt.name()] = ve;
}
ve = nullptr;
}
if (ve) {
applyVariantEdit(ve, vt);
}
if (now_label) {
label_labels[vt.name()]->setStyleSheet(PI2QString(vt.attribute(Attribute::style).toString()));
}
}
auto * cl = comm_labels.value(vn, nullptr);
if (cl) cl->setText(PIVariantEditorBase::vtTr(vt.comment()));
actionChange(button, vn);
return;
}
if (a == widget_params->actionReorder) {
if (!widget_reorder->showFor(current)) return;
grid->reorder(widget_reorder->map);
auto cl = current.children();
current.clearChildren();
for (int i = 0; i < cl.size_s(); ++i) {
int mi = widget_reorder->map.value(i, i);
if (mi < 0 || mi >= cl.size_s()) continue;
current.addChild(cl[mi]);
}
actionReorder(button, vn);
return;
}
if (a == widget_params->actionCut) {
actionCopy(button, vn);
actionRemove(button, vn);
return;
}
if (a == widget_params->actionCopy) {
actionCopy(button, vn);
return;
}
if (a == widget_params->actionPasteBefore) {
actionPaste(button, vn, 0);
return;
}
if (a == widget_params->actionPasteAfter) {
actionPaste(button, vn, 1);
return;
}
setGrouping((Grouping)a->data().toInt());
}
void PIValueTreeEdit::checkActions() {
bool can_paste = true;
PIString str = Q2PIString(QApplication::clipboard()->text());
if (str.isEmpty() || !str.startsWith("{")) {
can_paste = false;
} else {
PIJSON j = PIJSON::fromJSON(str);
if (!j.contains("name"))
can_paste = false;
else {
if (current.contains(j["name"].value().toString())) can_paste = false;
}
}
widget_params->actionPasteBefore->setEnabled(can_paste);
widget_params->actionPasteAfter->setEnabled(can_paste);
}
PIValueTreeEdit * PIValueTreeEdit::addTreeEdit(const PIValueTree & vt) {
auto * ve = new PIValueTreeEdit();
PIStringList rp = root_path;
@@ -528,24 +621,21 @@ PIValueTreeEdit::GridWidgets::GridWidgets(PIValueTreeEdit * p) {
a->setSeparator(true);
return a;
};
menu_group.addActions({p->widget_params->actionRename,
p->widget_params->actionReorder,
p->widget_params->menu_grouping.menuAction(),
newSeparator(),
p->widget_params->actionRemove});
menu_conf.addActions({p->widget_params->actionRename,
p->widget_params->actionChange,
p->widget_params->actionReorder,
newSeparator(),
p->widget_params->actionRemove});
menu_new.addActions({p->widget_params->actionValue, p->widget_params->actionGroup, p->widget_params->actionArray});
auto * wp = p->widget_params;
auto common_actions =
{newSeparator(), wp->actionCut, wp->actionCopy, wp->actionPasteBefore, wp->actionPasteAfter, newSeparator(), wp->actionRemove};
menu_group.addActions({wp->actionRename, wp->actionReorder, wp->menu_grouping.menuAction()});
menu_conf.addActions({wp->actionRename, wp->actionChange, wp->actionReorder});
menu_group.addActions(common_actions);
menu_conf.addActions(common_actions);
menu_new.addActions({wp->actionValue, wp->actionGroup, wp->actionArray});
button_add = new QToolButton();
button_add->setIcon(QIcon(":/icons/list-add.png"));
button_add->setPopupMode(QToolButton::InstantPopup);
button_add->setMenu(&menu_new);
p->widget_params->actionValue->setData((int)NewType::Value);
p->widget_params->actionGroup->setData((int)NewType::Group);
p->widget_params->actionArray->setData((int)NewType::Array);
wp->actionValue->setData((int)NewType::Value);
wp->actionGroup->setData((int)NewType::Group);
wp->actionArray->setData((int)NewType::Array);
connect(button_add, &QToolButton::triggered, this, [this](QAction * a) { parent->newRequest((NewType)a->data().toInt()); });
}
@@ -612,12 +702,14 @@ void PIValueTreeEdit::GridWidgets::addRow(QWidget * w) {
QToolButton * PIValueTreeEdit::GridWidgets::createConfigButton(const PIValueTree & vt, bool is_group) {
auto * b = new QToolButton();
auto * b = new ToolButton();
b->setIcon(icon_conf);
b->setPopupMode(QToolButton::InstantPopup);
b->setMenu((is_group && !vt.isArray()) ? &menu_group : &menu_conf);
b->setProperty(property_name, PI2QString(vt.name()));
b->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
b->setPressHandler([this]() { parent->checkActions(); });
// connect(b, &QToolButton::pressed, parent, &PIValueTreeEdit::checkActions);
connect(b, &QToolButton::triggered, this, [this, b](QAction * a) {
parent->actionTriggered(b, Q2PIString(b->property(property_name).toString()), a);
});

View File

@@ -80,7 +80,14 @@ private:
void removeAll();
void build();
void applyValues() const;
void actionRename(QToolButton * button, const PIString & vn);
void actionRemove(QToolButton * button, const PIString & vn);
void actionChange(QToolButton * button, const PIString & vn);
void actionReorder(QToolButton * button, const PIString & vn);
void actionCopy(QToolButton * button, const PIString & vn);
void actionPaste(QToolButton * button, const PIString & vn, int offset);
void actionTriggered(QToolButton * button, const PIString & vn, QAction * a);
void checkActions();
void newRequest(NewType type);
PIValueTreeEdit * addTreeEdit(const PIValueTree & vt);
PIValueTreeEdit * rootTreeEdit();

View File

@@ -167,6 +167,48 @@
<string>Reorder ...</string>
</property>
</action>
<action name="actionCopy">
<property name="icon">
<iconset resource="../application/qad_application.qrc">
<normaloff>:/icons/edit-copy.png</normaloff>:/icons/edit-copy.png</iconset>
</property>
<property name="text">
<string>Copy</string>
</property>
</action>
<action name="actionCut">
<property name="icon">
<iconset resource="qad_piqt_widgets.qrc">
<normaloff>:/icons/edit-cut.png</normaloff>:/icons/edit-cut.png</iconset>
</property>
<property name="text">
<string>Cut</string>
</property>
</action>
<action name="actionPasteBefore">
<property name="icon">
<iconset resource="../utils/qad_utils.qrc">
<normaloff>:/icons/edit-paste.png</normaloff>:/icons/edit-paste.png</iconset>
</property>
<property name="text">
<string>Paste before</string>
</property>
<property name="toolTip">
<string>Paste before</string>
</property>
</action>
<action name="actionPasteAfter">
<property name="icon">
<iconset resource="../utils/qad_utils.qrc">
<normaloff>:/icons/edit-paste.png</normaloff>:/icons/edit-paste.png</iconset>
</property>
<property name="text">
<string>Paste after</string>
</property>
<property name="toolTip">
<string>Paste after</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
@@ -185,6 +227,7 @@
<include location="../blockview/qad_blockview.qrc"/>
<include location="../graphic/qad_graphic.qrc"/>
<include location="qad_piqt_widgets.qrc"/>
<include location="../utils/qad_utils.qrc"/>
</resources>
<connections>
<connection>

View File

@@ -45,76 +45,8 @@
</widget>
</item>
</layout>
<action name="actionRemove">
<property name="icon">
<iconset resource="../blockview/qad_blockview.qrc">
<normaloff>:/icons/edit-delete.png</normaloff>:/icons/edit-delete.png</iconset>
</property>
<property name="text">
<string>Remove</string>
</property>
</action>
<action name="actionChange">
<property name="icon">
<iconset resource="../application/qad_application.qrc">
<normaloff>:/icons/configure.png</normaloff>:/icons/configure.png</iconset>
</property>
<property name="text">
<string>Change ...</string>
</property>
</action>
<action name="actionRename">
<property name="icon">
<iconset resource="../graphic/qad_graphic.qrc">
<normaloff>:/icons/border-line.png</normaloff>:/icons/border-line.png</iconset>
</property>
<property name="text">
<string>Rename ...</string>
</property>
</action>
<action name="actionValue">
<property name="icon">
<iconset resource="qad_piqt_widgets.qrc">
<normaloff>:/icons/code-variable.png</normaloff>:/icons/code-variable.png</iconset>
</property>
<property name="text">
<string>Value</string>
</property>
</action>
<action name="actionGroup">
<property name="icon">
<iconset resource="qad_piqt_widgets.qrc">
<normaloff>:/icons/code-struct.png</normaloff>:/icons/code-struct.png</iconset>
</property>
<property name="text">
<string>Group</string>
</property>
</action>
<action name="actionArray">
<property name="icon">
<iconset resource="qad_piqt_widgets.qrc">
<normaloff>:/icons/code-union.png</normaloff>:/icons/code-union.png</iconset>
</property>
<property name="text">
<string>Array</string>
</property>
</action>
<action name="actionReorder">
<property name="icon">
<iconset resource="../graphic/qad_graphic.qrc">
<normaloff>:/icons/legend.png</normaloff>:/icons/legend.png</iconset>
</property>
<property name="text">
<string>Reorder ...</string>
</property>
</action>
</widget>
<resources>
<include location="../application/qad_application.qrc"/>
<include location="../blockview/qad_blockview.qrc"/>
<include location="../graphic/qad_graphic.qrc"/>
<include location="qad_piqt_widgets.qrc"/>
</resources>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>

View File

@@ -74,6 +74,12 @@
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
@@ -116,74 +122,10 @@
</widget>
</item>
</layout>
<action name="actionRemove">
<property name="icon">
<iconset resource="../blockview/qad_blockview.qrc">
<normaloff>:/icons/edit-delete.png</normaloff>:/icons/edit-delete.png</iconset>
</property>
<property name="text">
<string>Remove</string>
</property>
</action>
<action name="actionChange">
<property name="icon">
<iconset resource="../application/qad_application.qrc">
<normaloff>:/icons/configure.png</normaloff>:/icons/configure.png</iconset>
</property>
<property name="text">
<string>Change ...</string>
</property>
</action>
<action name="actionRename">
<property name="icon">
<iconset resource="../graphic/qad_graphic.qrc">
<normaloff>:/icons/border-line.png</normaloff>:/icons/border-line.png</iconset>
</property>
<property name="text">
<string>Rename ...</string>
</property>
</action>
<action name="actionValue">
<property name="icon">
<iconset resource="qad_piqt_widgets.qrc">
<normaloff>:/icons/code-variable.png</normaloff>:/icons/code-variable.png</iconset>
</property>
<property name="text">
<string>Value</string>
</property>
</action>
<action name="actionGroup">
<property name="icon">
<iconset resource="qad_piqt_widgets.qrc">
<normaloff>:/icons/code-struct.png</normaloff>:/icons/code-struct.png</iconset>
</property>
<property name="text">
<string>Group</string>
</property>
</action>
<action name="actionArray">
<property name="icon">
<iconset resource="qad_piqt_widgets.qrc">
<normaloff>:/icons/code-union.png</normaloff>:/icons/code-union.png</iconset>
</property>
<property name="text">
<string>Array</string>
</property>
</action>
<action name="actionReorder">
<property name="icon">
<iconset resource="../graphic/qad_graphic.qrc">
<normaloff>:/icons/legend.png</normaloff>:/icons/legend.png</iconset>
</property>
<property name="text">
<string>Reorder ...</string>
</property>
</action>
</widget>
<resources>
<include location="../application/qad_application.qrc"/>
<include location="../blockview/qad_blockview.qrc"/>
<include location="../graphic/qad_graphic.qrc"/>
<include location="qad_piqt_widgets.qrc"/>
</resources>
<connections>

View File

@@ -1,5 +1,6 @@
<RCC>
<qresource prefix="/">
<file>../../icons/edit-cut.png</file>
<file>../../icons/configure.png</file>
<file>../../icons/border-line.png</file>
<file>../../icons/list-add.png</file>

View File

@@ -4,6 +4,7 @@
#include <QDebug>
#include <QDir>
#include <QDirIterator>
#include <QLibraryInfo>
#include <QTranslator>
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
# include <QStandardPaths>
@@ -74,12 +75,22 @@ void QAD::loadTranslations(QString lang) {
if (lang.isEmpty()) lang = QLocale().bcp47Name();
QString short_lang = lang.left(2);
QStringList dirs = resourcePaths("lang");
QString std_dir = QDir::cleanPath(QLibraryInfo::location(QLibraryInfo::TranslationsPath));
bool has_std = false;
for (const QString & d: dirs) {
if (QDir::cleanPath(d) == std_dir) {
has_std = true;
break;
}
}
if (!has_std) dirs << std_dir;
for (const QString & d: dirs) {
QDirIterator dit(d);
while (dit.hasNext()) {
dit.next();
if (!dit.filePath().endsWith(".qm")) continue;
if (!dit.fileInfo().baseName().endsWith(lang) && !dit.fileInfo().baseName().endsWith(short_lang)) continue;
if (dit.fileName() == QString("qt_%1.qm").arg(short_lang)) continue;
QTranslator * tr = new QTranslator();
if (tr->load(dit.filePath())) {
qApp->installTranslator(tr);