nodeview: associate disconnected nodes with selected blocks until re-connected
This commit is contained in:
@@ -21,8 +21,6 @@ set(OLIVE_SOURCES
|
||||
widget/nodeview/nodeviewcommon.h
|
||||
widget/nodeview/nodeviewedge.h
|
||||
widget/nodeview/nodeviewedge.cpp
|
||||
widget/nodeview/nodeviewfilter.h
|
||||
widget/nodeview/nodeviewfilter.cpp
|
||||
widget/nodeview/nodeviewitem.h
|
||||
widget/nodeview/nodeviewitem.cpp
|
||||
widget/nodeview/nodeviewitemwidgetproxy.h
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include "core.h"
|
||||
#include "nodeviewundo.h"
|
||||
#include "node/factory.h"
|
||||
#include "nodeviewfilter.h"
|
||||
#include "widget/menu/menushared.h"
|
||||
|
||||
#define super HandMovableView
|
||||
@@ -44,6 +43,7 @@ NodeView::NodeView(QWidget *parent) :
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
setMouseTracking(true);
|
||||
setRenderHint(QPainter::Antialiasing);
|
||||
setViewportUpdateMode(FullViewportUpdate);
|
||||
|
||||
connect(&scene_, &QGraphicsScene::changed, this, &NodeView::ItemsChanged);
|
||||
connect(this, &NodeView::customContextMenuRequested, this, &NodeView::ShowContextMenu);
|
||||
@@ -51,6 +51,8 @@ NodeView::NodeView(QWidget *parent) :
|
||||
ConnectSelectionChangedSignal();
|
||||
|
||||
SetFlowDirection(NodeViewCommon::kTopToBottom);
|
||||
|
||||
scene_.setSceneRect(-1000000, -1000000, 2000000, 2000000);
|
||||
}
|
||||
|
||||
NodeView::~NodeView()
|
||||
@@ -66,10 +68,10 @@ void NodeView::SetGraph(NodeGraph *graph)
|
||||
}
|
||||
|
||||
if (graph_ != nullptr) {
|
||||
disconnect(graph_, &NodeGraph::NodeAdded, this, &NodeView::AddNode);
|
||||
disconnect(graph_, &NodeGraph::NodeAdded, &scene_, &NodeViewScene::AddNode);
|
||||
disconnect(graph_, &NodeGraph::NodeRemoved, &scene_, &NodeViewScene::RemoveNode);
|
||||
disconnect(graph_, &NodeGraph::EdgeAdded, this, &NodeView::AddEdge);
|
||||
disconnect(graph_, &NodeGraph::EdgeRemoved, &scene_, &NodeViewScene::RemoveEdge);
|
||||
disconnect(graph_, &NodeGraph::EdgeAdded, this, &NodeView::GraphEdgeAdded);
|
||||
disconnect(graph_, &NodeGraph::EdgeRemoved, this, &NodeView::GraphEdgeRemoved);
|
||||
}
|
||||
|
||||
// Clear the scene of all UI objects
|
||||
@@ -81,12 +83,16 @@ void NodeView::SetGraph(NodeGraph *graph)
|
||||
|
||||
// If the graph is valid, add UI objects for each of its Nodes
|
||||
if (graph_ != nullptr) {
|
||||
connect(graph_, &NodeGraph::NodeAdded, this, &NodeView::AddNode);
|
||||
connect(graph_, &NodeGraph::NodeAdded, &scene_, &NodeViewScene::AddNode);
|
||||
connect(graph_, &NodeGraph::NodeRemoved, &scene_, &NodeViewScene::RemoveNode);
|
||||
connect(graph_, &NodeGraph::EdgeAdded, this, &NodeView::AddEdge);
|
||||
connect(graph_, &NodeGraph::EdgeRemoved, &scene_, &NodeViewScene::RemoveEdge);
|
||||
connect(graph_, &NodeGraph::EdgeAdded, this, &NodeView::GraphEdgeAdded);
|
||||
connect(graph_, &NodeGraph::EdgeRemoved, this, &NodeView::GraphEdgeRemoved);
|
||||
|
||||
AddNodes(graph_->nodes());
|
||||
foreach (Node* n, graph_->nodes()) {
|
||||
scene_.AddNode(n);
|
||||
}
|
||||
|
||||
ValidateFilter();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +109,9 @@ void NodeView::DeleteSelected()
|
||||
|
||||
foreach (NodeEdge* edge, selected_edges) {
|
||||
new NodeEdgeRemoveCommand(edge->output(), edge->input(), command);
|
||||
qDebug() << "Deleting edge between" << edge->output()->parentNode() << "and" << edge->input()->parentNode();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
@@ -184,6 +192,13 @@ void NodeView::SelectWithDependencies(QList<Node *> nodes)
|
||||
|
||||
void NodeView::SelectBlocks(const QList<Block *> &blocks)
|
||||
{
|
||||
// Remove temporary associations
|
||||
foreach (Block* b, selected_blocks_) {
|
||||
if (!blocks.contains(b)) {
|
||||
temporary_association_map_.remove(b);
|
||||
}
|
||||
}
|
||||
|
||||
selected_blocks_ = blocks;
|
||||
|
||||
if (filter_mode_ == kFilterShowSelectedBlocks) {
|
||||
@@ -199,6 +214,13 @@ void NodeView::SelectBlocks(const QList<Block *> &blocks)
|
||||
}
|
||||
|
||||
SelectWithDependencies(nodes);
|
||||
|
||||
if (!blocks.isEmpty()) {
|
||||
NodeViewItem* item = scene_.NodeToUIObject(blocks.first());
|
||||
if (item) {
|
||||
centerOn(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::CopySelected(bool cut)
|
||||
@@ -294,7 +316,7 @@ void NodeView::ItemsChanged()
|
||||
{
|
||||
QHash<NodeEdge *, NodeViewEdge *>::const_iterator i;
|
||||
|
||||
for (i=scene_.edge_map().begin(); i!=scene_.edge_map().end(); i++) {
|
||||
for (i=scene_.edge_map().constBegin(); i!=scene_.edge_map().constEnd(); i++) {
|
||||
i.value()->Adjust();
|
||||
}
|
||||
}
|
||||
@@ -528,6 +550,10 @@ void NodeView::CreateNodeSlot(QAction *action)
|
||||
Node* new_node = NodeFactory::CreateFromMenuAction(action);
|
||||
|
||||
if (new_node) {
|
||||
// Associate this new node with these blocks (allows it to be visible with them even while it
|
||||
// isn't connected to anything)
|
||||
AssociateNodeWithSelectedBlocks(new_node);
|
||||
|
||||
Core::instance()->undo_stack()->push(new NodeAddCommand(graph_, new_node));
|
||||
|
||||
NodeViewItem* item = scene_.NodeToUIObject(new_node);
|
||||
@@ -573,12 +599,6 @@ void NodeView::ContextMenuLabelNode()
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::ContextMenuShowFiltersDialog()
|
||||
{
|
||||
NodeViewFilterDialog fd(this);
|
||||
fd.exec();
|
||||
}
|
||||
|
||||
void NodeView::ContextMenuFilterChanged(QAction *action)
|
||||
{
|
||||
FilterMode filter = static_cast<FilterMode>(action->data().toInt());
|
||||
@@ -807,21 +827,17 @@ void NodeView::UpdateBlockFilter()
|
||||
bool first = true;
|
||||
QRectF last_rect;
|
||||
|
||||
QList<Node*> all_nodes;
|
||||
QList<Node*> currently_visible;
|
||||
|
||||
foreach (Block* b, selected_blocks_) {
|
||||
// Auto-position this node's dependencies
|
||||
scene_.ReorganizeFrom(b);
|
||||
|
||||
// Start calculating the bounding rect of this node's deps
|
||||
QPointF node_pos = b->GetPosition();
|
||||
QRectF anchor(node_pos, node_pos);
|
||||
|
||||
all_nodes.append(b);
|
||||
|
||||
QList<Node*> deps = b->GetDependencies();
|
||||
all_nodes.append(deps);
|
||||
|
||||
// Show nodes that are block dependencies
|
||||
scene_.NodeToUIObject(b)->setVisible(true);
|
||||
|
||||
foreach (Node* d, deps) {
|
||||
QPointF dep_pos = d->GetPosition();
|
||||
@@ -831,9 +847,13 @@ void NodeView::UpdateBlockFilter()
|
||||
anchor.setTop(qMin(anchor.top(), dep_pos.y()));
|
||||
anchor.setBottom(qMax(anchor.bottom(), dep_pos.y()));
|
||||
|
||||
scene_.NodeToUIObject(d)->setVisible(true);
|
||||
NodeViewItem* item = scene_.NodeToUIObject(d);
|
||||
if (item) {
|
||||
item->setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Shift the bounding rect in relation to the other bounding rects
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
@@ -847,37 +867,53 @@ void NodeView::UpdateBlockFilter()
|
||||
}
|
||||
}
|
||||
|
||||
// Now that we're done calculating the bounding rect, add this block...
|
||||
deps.append(b);
|
||||
|
||||
// ...then add its associations
|
||||
deps.append(temporary_association_map_[b]);
|
||||
QHash<Node*, QList<Block*> >::const_iterator i;
|
||||
for (i=association_map_.begin(); i!=association_map_.end(); i++) {
|
||||
if (i.value().contains(b)) {
|
||||
deps.append(i.key());
|
||||
}
|
||||
}
|
||||
|
||||
// And make sure all nodes are shown
|
||||
foreach (Node* n, deps) {
|
||||
NodeViewItem* item = scene_.NodeToUIObject(n);
|
||||
if (item) {
|
||||
item->setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
// And lastly, add them all to our currently visible list
|
||||
currently_visible.append(deps);
|
||||
|
||||
// Cache this rect so we can calculate other rects
|
||||
last_rect = anchor;
|
||||
}
|
||||
|
||||
// Show only edges between those dependencies
|
||||
foreach (NodeViewEdge* edge, scene_.edge_map()) {
|
||||
edge->setVisible((all_nodes.contains(edge->edge()->input()->parentNode())
|
||||
&& all_nodes.contains(edge->edge()->output()->parentNode())));
|
||||
edge->setVisible((currently_visible.contains(edge->edge()->input()->parentNode())
|
||||
&& currently_visible.contains(edge->edge()->output()->parentNode())));
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::AddNodes(const QList<Node *> node)
|
||||
void NodeView::AssociateNodeWithSelectedBlocks(Node *n)
|
||||
{
|
||||
foreach (Node* n, node) {
|
||||
scene_.AddNode(n);
|
||||
association_map_.insert(n, selected_blocks_);
|
||||
connect(n, &Node::destroyed, this, &NodeView::AssociatedNodeDestroyed, Qt::DirectConnection);
|
||||
}
|
||||
|
||||
void NodeView::DisassociateNode(Node *n, bool remove_from_map)
|
||||
{
|
||||
if (remove_from_map) {
|
||||
association_map_.remove(n);
|
||||
}
|
||||
|
||||
ValidateFilter();
|
||||
}
|
||||
|
||||
void NodeView::AddNode(Node *node)
|
||||
{
|
||||
scene_.AddNode(node);
|
||||
|
||||
ValidateFilter();
|
||||
}
|
||||
|
||||
void NodeView::AddEdge(NodeEdgePtr edge)
|
||||
{
|
||||
scene_.AddEdge(edge);
|
||||
|
||||
ValidateFilter();
|
||||
disconnect(n, &Node::destroyed, this, &NodeView::AssociatedNodeDestroyed);
|
||||
}
|
||||
|
||||
void NodeView::ValidateFilter()
|
||||
@@ -894,4 +930,62 @@ void NodeView::ValidateFilter()
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::AssociatedNodeDestroyed()
|
||||
{
|
||||
DisassociateNode(static_cast<Node*>(sender()), true);
|
||||
}
|
||||
|
||||
void NodeView::GraphEdgeAdded(NodeEdgePtr edge)
|
||||
{
|
||||
Node* input_node = edge->input()->parentNode();
|
||||
|
||||
if (input_node->OutputsTo(static_cast<Sequence*>(graph_)->viewer_output(), true)) {
|
||||
QHash<Node*, QList<Block*> >::const_iterator i = association_map_.begin();
|
||||
|
||||
while (i != association_map_.end()) {
|
||||
if (input_node->InputsFrom(i.key(), true)) {
|
||||
i = association_map_.erase(i);
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scene_.AddEdge(edge);
|
||||
|
||||
ValidateFilter();
|
||||
}
|
||||
|
||||
void NodeView::GraphEdgeRemoved(NodeEdgePtr edge)
|
||||
{
|
||||
scene_.RemoveEdge(edge);
|
||||
|
||||
Node* output_node = edge->output()->parentNode();
|
||||
|
||||
// Check if this disconnected node still connects to a selected block, in which case do nothing
|
||||
foreach (Block* b, selected_blocks_) {
|
||||
if (output_node->OutputsTo(b, true)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QList<Node*> disconnected_nodes;
|
||||
disconnected_nodes.append(output_node);
|
||||
disconnected_nodes.append(output_node->GetDependencies());
|
||||
|
||||
if (output_node->OutputsTo(static_cast<Sequence*>(graph_)->viewer_output(), true)) {
|
||||
// Check if this disconnected node still has a path to the viewer somewhere else
|
||||
foreach (Block* b, selected_blocks_) {
|
||||
QList<Node*>& temp_assocs = temporary_association_map_[b];
|
||||
|
||||
temp_assocs.append(disconnected_nodes);
|
||||
}
|
||||
} else {
|
||||
// Otherwise, we must associate these nodes
|
||||
foreach (Node* n, disconnected_nodes) {
|
||||
AssociateNodeWithSelectedBlocks(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -102,6 +102,9 @@ private:
|
||||
|
||||
void UpdateBlockFilter();
|
||||
|
||||
void AssociateNodeWithSelectedBlocks(Node* n);
|
||||
void DisassociateNode(Node* n, bool remove_from_map);
|
||||
|
||||
NodeGraph* graph_;
|
||||
|
||||
struct AttachedItem {
|
||||
@@ -118,6 +121,10 @@ private:
|
||||
|
||||
QList<Block*> selected_blocks_;
|
||||
|
||||
QHash<Node*, QList<Block*> > association_map_;
|
||||
|
||||
QHash<Block*, QList<Node*> > temporary_association_map_;
|
||||
|
||||
enum FilterMode {
|
||||
kFilterShowAll,
|
||||
kFilterShowSelectedBlocks
|
||||
@@ -126,12 +133,14 @@ private:
|
||||
FilterMode filter_mode_;
|
||||
|
||||
private slots:
|
||||
void AddNodes(const QList<Node*> node);
|
||||
void AddNode(Node* node);
|
||||
void AddEdge(NodeEdgePtr edge);
|
||||
|
||||
void ValidateFilter();
|
||||
|
||||
void AssociatedNodeDestroyed();
|
||||
|
||||
void GraphEdgeAdded(NodeEdgePtr edge);
|
||||
|
||||
void GraphEdgeRemoved(NodeEdgePtr edge);
|
||||
|
||||
/**
|
||||
* @brief Internal function triggered when any change is signalled from the QGraphicsScene
|
||||
*
|
||||
@@ -169,11 +178,6 @@ private slots:
|
||||
*/
|
||||
void ContextMenuLabelNode();
|
||||
|
||||
/**
|
||||
* @brief Receiver that shows the filters dialog
|
||||
*/
|
||||
void ContextMenuShowFiltersDialog();
|
||||
|
||||
/**
|
||||
* @brief Receiver for the user changing the filter
|
||||
*/
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "nodeviewfilter.h"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QGroupBox>
|
||||
#include <QLayoutItem>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#define FILTER_COLUMN_COUNT 7
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
NodeViewFilterDialog::NodeViewFilterDialog(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
row_count_(0)
|
||||
{
|
||||
setWindowTitle(tr("Configure Node Filters"));
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||
|
||||
{
|
||||
QGroupBox* filter_group = new QGroupBox();
|
||||
filter_group->setTitle(tr("Filters"));
|
||||
|
||||
QVBoxLayout* filter_outer_layout = new QVBoxLayout(filter_group);
|
||||
filter_layout_ = new QGridLayout();
|
||||
filter_layout_->setMargin(0);
|
||||
filter_outer_layout->addLayout(filter_layout_);
|
||||
filter_outer_layout->addStretch();
|
||||
|
||||
layout->addWidget(filter_group);
|
||||
}
|
||||
|
||||
plus_btn_ = new QPushButton(tr("+"));
|
||||
plus_btn_->setFixedWidth(plus_btn_->sizeHint().height());
|
||||
connect(plus_btn_, &QPushButton::clicked, this, &NodeViewFilterDialog::AppendRow);
|
||||
|
||||
AppendRow();
|
||||
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
layout->addWidget(buttons);
|
||||
}
|
||||
|
||||
void NodeViewFilterDialog::AppendRow()
|
||||
{
|
||||
QCheckBox* enabled_check = new QCheckBox();
|
||||
enabled_check->setChecked(true);
|
||||
filter_layout_->addWidget(enabled_check, row_count_, 0);
|
||||
|
||||
QComboBox* showhide_combo = new QComboBox();
|
||||
showhide_combo->addItem(tr("Show"));
|
||||
showhide_combo->addItem(tr("Hide"));
|
||||
filter_layout_->addWidget(showhide_combo, row_count_, 1);
|
||||
|
||||
QComboBox* type_combo = new QComboBox();
|
||||
type_combo->addItem(tr("Any"));
|
||||
type_combo->addItem(tr("Selected"));
|
||||
filter_layout_->addWidget(type_combo, row_count_, 2);
|
||||
|
||||
QComboBox* from_combo = new QComboBox();
|
||||
from_combo->addItem(tr("From Node"));
|
||||
from_combo->addItem(tr("From Category"));
|
||||
filter_layout_->addWidget(from_combo, row_count_, 3);
|
||||
|
||||
QComboBox* from_entry_combo = new QComboBox();
|
||||
filter_layout_->addWidget(from_entry_combo, row_count_, 4);
|
||||
|
||||
QComboBox* to_entry_combo = new QComboBox();
|
||||
filter_layout_->addWidget(to_entry_combo, row_count_, 5);
|
||||
|
||||
QPushButton* minus_btn = new QPushButton(tr("-"));
|
||||
minus_btn->setFixedWidth(minus_btn->sizeHint().height());
|
||||
connect(minus_btn, &QPushButton::clicked, this, &NodeViewFilterDialog::RemoveRowButtonClicked);
|
||||
filter_layout_->addWidget(minus_btn, row_count_, 6);
|
||||
|
||||
row_count_++;
|
||||
|
||||
UpdateAddButtonPos();
|
||||
}
|
||||
|
||||
void NodeViewFilterDialog::RemoveRowButtonClicked()
|
||||
{
|
||||
for (int i=0;i<row_count_;i++) {
|
||||
if (filter_layout_->itemAtPosition(i, FILTER_COLUMN_COUNT-1)->widget() == sender()) {
|
||||
RemoveRow(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeViewFilterDialog::RemoveRow(int row)
|
||||
{
|
||||
for (int i=0;i<FILTER_COLUMN_COUNT;i++) {
|
||||
delete filter_layout_->itemAtPosition(row, i)->widget();
|
||||
}
|
||||
|
||||
for (int j=row+1;j<row_count_;j++) {
|
||||
for (int i=0;i<FILTER_COLUMN_COUNT;i++) {
|
||||
QWidget* w = filter_layout_->itemAtPosition(j, i)->widget();
|
||||
|
||||
filter_layout_->addWidget(w, j - 1, i);
|
||||
}
|
||||
}
|
||||
|
||||
row_count_--;
|
||||
|
||||
UpdateAddButtonPos();
|
||||
}
|
||||
|
||||
void NodeViewFilterDialog::UpdateAddButtonPos()
|
||||
{
|
||||
filter_layout_->addWidget(plus_btn_, row_count_, FILTER_COLUMN_COUNT - 1);
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
@@ -1,56 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef NODEVIEWFILTERDIALOG_H
|
||||
#define NODEVIEWFILTERDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QGridLayout>
|
||||
|
||||
#include "node/node.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
class NodeViewFilterDialog : public QDialog
|
||||
{
|
||||
public:
|
||||
NodeViewFilterDialog(QWidget* parent = nullptr);
|
||||
|
||||
private:
|
||||
void RemoveRow(int row);
|
||||
|
||||
void UpdateAddButtonPos();
|
||||
|
||||
int row_count_;
|
||||
|
||||
QGridLayout* filter_layout_;
|
||||
|
||||
QPushButton* plus_btn_;
|
||||
|
||||
private slots:
|
||||
void AppendRow();
|
||||
|
||||
void RemoveRowButtonClicked();
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
#endif // NODEVIEWFILTERDIALOG_H
|
||||
Reference in New Issue
Block a user