Initial commit

This commit is contained in:
torsten committed 2026-08-13 18:33:10 +02:00
commit b7d90b9afe
16245 files changed
+3396666

No files matched your search

@@ -0,0 +1,104 @@
/**
* @RDE_HEADER@
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Last, NLSE, 2018-01-11
*/
#include "evl_comment_model.h"
#include <QDateTime>
#include <QColor>
#include <LtsSession/lts_session_manager.h>
#include <ngltools/ngltools_dtg.h>
namespace Evaluations
{
EvlCommentDataViewModel::EvlCommentDataViewModel(QObject *parent) : LtsAbstractTableModel(
{
tr("Id"),
tr("Timestamp"),
tr("Sender"),
tr("Is personal"),
tr("Comment"),
tr("Hierarchy")
},
parent)
{
}
QVariant EvlCommentDataViewModel::data(const QModelIndex &index, int role) const
{
if (!indexIsValid(index))
return QVariant();
if (role == Qt::BackgroundRole)
{
const CommentTuple &row = m_vecData.at(index.row());
if (row.m_receiver.size())
return QColor(188, 210, 238);
}
if (role == Qt::DisplayRole)
{
const CommentTuple &row = m_vecData.at(index.row());
switch (index.column())
{
case Id:
return index.row() + 1;
case Timestamp:
return QDateTime::fromMSecsSinceEpoch(row.m_timestamp.currentMSecsSinceEpoch());
case Sender:
return row.m_sender;
case IsPersonal:
return row.m_receiver.size() ? tr("Yes") : tr("No");
case Comment:
return row.m_text;
case Hierarchy:
return row.m_hierarchy;
default:
break;
}
}
return QVariant();
}
void EvlCommentDataViewProxyModel::setSourceModel(QAbstractItemModel *sourceModel)
{
m_sourceModel = qobject_cast<EvlCommentDataViewModel*>(sourceModel);
LtsWidgets::LtsColumnFilterProxyModel::setSourceModel(sourceModel);
}
bool EvlCommentDataViewProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
{
if (!m_sourceModel ||
source_left.column() != source_right.column() ||
!source_left.isValid() ||
!source_right.isValid())
return LtsWidgets::LtsColumnFilterProxyModel::lessThan(source_left, source_right);
const auto &dataLeft = m_sourceModel->m_vecData.at(source_left.row());
const auto dataRight = m_sourceModel->m_vecData.at(source_right.row());
switch (source_left.column())
{
case EvlCommentDataViewModel::Timestamp:
return dataLeft.m_timestamp.currentMSecsSinceEpoch() < dataRight.m_timestamp.currentMSecsSinceEpoch();
case EvlCommentDataViewModel::Id:
case EvlCommentDataViewModel::Sender:
case EvlCommentDataViewModel::IsPersonal:
case EvlCommentDataViewModel::Comment:
case EvlCommentDataViewModel::Hierarchy:
break;
}
return LtsWidgets::LtsColumnFilterProxyModel::lessThan(source_left, source_right);
}
}
+53
View File
@@ -0,0 +1,53 @@
/**
* @RDE_HEADER@
*
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Last, NLSE, 2018-01-11
*/
#pragma once
#include <LtsWidgets/lts_abstract_table_model.h>
#include <LtsWidgets/lts_column_filter_proxy_model.h>
#include "evl_comment_tuple.h"
namespace Evaluations
{
class ForeignDataInterface;
class CommunicationInterface;
class EvlCommentDataViewModel : public LtsWidgets::LtsAbstractTableModel<CommentTuple>
{
Q_OBJECT
enum columns
{
Id,
Timestamp,
Sender,
IsPersonal,
Comment,
Hierarchy
};
public:
explicit EvlCommentDataViewModel(QObject *parent = nullptr);
QVariant data(const QModelIndex &index, int role) const override;
friend class EvlCommentDataViewProxyModel;
};
class EvlCommentDataViewProxyModel final : public LtsWidgets::LtsColumnFilterProxyModel
{
Q_OBJECT
public:
using LtsWidgets::LtsColumnFilterProxyModel::LtsColumnFilterProxyModel;
void setSourceModel(QAbstractItemModel *sourceModel) override;
protected:
bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override;
private:
EvlCommentDataViewModel *m_sourceModel{nullptr};
};
}
+29
View File
@@ -0,0 +1,29 @@
/**
* @RDE_HEADER@
*
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Last, NLSE, 2018-01-11
*/
#pragma once
#include <QString>
#include <QDateTime>
namespace Evaluations
{
struct CommentTuple
{
int m_commentId = 0;
QDateTime m_timestamp;
QString m_sender;
QString m_receiver;
QString m_text;
QString m_hierarchy;
// id schema?
};
}
+150
View File
@@ -0,0 +1,150 @@
/**
* @RDE_HEADER@
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Last, NLSE, 2018-01-11
*/
#include "evl_comments.h"
#include "ui_evl_comments.h"
#include "evl_comment_model.h"
#include <LtsSession/lts_session_data.h>
#include <PluginInterfaces/data_plugin_interface.h>
#include <QKeyEvent>
namespace Evaluations
{
EvlCommentsView::EvlCommentsView(const std::shared_ptr<Interface::Dtc> &evlDtc,
const std::shared_ptr<OperatorManagement::Interface::Dtc> &opmDtc,
QWidget *parent)
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::CloseOnly),
m_ui(new Ui::EvlCommentsDialog),
m_evlDtc(evlDtc),
m_opmDtc(opmDtc),
m_logger(getLogger("EvlComments::EvlCommentsView"))
{
m_ui->setupUi(this);
setOnlineHelpFilename(QStringLiteral("Situation-Display_O-C_Evaluations"));
m_ui->m_tableComments->setSelectionMode(QAbstractItemView::SingleSelection);
connect(m_ui->m_tableComments, &LtsDataView::LtsDataView::signalDoubleClicked, this, &EvlCommentsView::onLtsDataView_DoubleClicked);
m_model = new EvlCommentDataViewModel(this);
m_proxyModel = new EvlCommentDataViewProxyModel(this);
m_proxyModel->setSourceModel(m_model);
m_ui->m_tableComments->setModel(m_proxyModel);
m_ui->m_tableComments->installEventFilter(this);
m_buttonBox->getButton(LtsWidgets::DialogButton::Ok)->hide();
m_buttonBox->getButton(LtsWidgets::DialogButton::Apply)->hide();
// Get historical comments
auto cb = [this](const Evaluations::EvaluationComment & c)
{
insertComment(c);
};
m_evlDtc->evaluationComments(cb);
// Subscribe for new comments
connect(m_evlDtc.get(), &Evaluations::Interface::Dtc::evaluationCommentChanged, this, &EvlCommentsView::onCommentChanged, Qt::UniqueConnection);
}
EvlCommentsView::~EvlCommentsView()
{
delete m_ui;
}
void EvlCommentsView::retranslate()
{
translate(m_ui, tr("Display O/C Evaluations"));
}
bool EvlCommentsView::eventFilter(QObject *object, QEvent *event)
{
if (object == m_ui->m_tableComments)
{
if (event->type() == QEvent::KeyPress)
{
QKeyEvent *pKeyEvent = static_cast<QKeyEvent *>(event);
// Enter: Enter on numpad
if (pKeyEvent->key() == Qt::Key_Return || pKeyEvent->key() == Qt::Key_Enter)
onLtsDataView_DoubleClicked();
}
}
return LtsWidgets::LtsDialog::eventFilter(object, event);
}
void EvlCommentsView::tableColumnOrganiserSetting()
{
//LtsColumnOrganiser
m_ui->m_tableComments->setColumnsOrganiserEnabled(identifier(), true);
}
void EvlCommentsView::onLtsDataView_DoubleClicked()
{
const QModelIndex idx = m_ui->m_tableComments->currentIndex();
if (idx.row() >= 0)
{
Evaluations::CommentTuple row = m_model->getRowData(idx.row());
LtsWidgets::LtsMessageDialog::showMessage(this, tr("Comment:") + QStringLiteral("\n") + row.m_text);
}
}
void EvlCommentsView::onCommentChanged(const EvaluationComment &data, DataModification modType)
{
switch (modType)
{
case DataModification::eAdded:
insertComment(data);
break;
default:
break;
}
}
bool EvlCommentsView::insertComment(const Evaluations::EvaluationComment &c)
{
QString strSenderOperatorName, strReceiverOperatorName;
OperatorManagement::User sender = m_opmDtc->user(c.senderOperatorId());
strSenderOperatorName = QString::fromStdString(sender.name());
if (c.receiverOperatorId().is_set())
{
// Add only if receiver id matches to the current operator!
OperatorManagement::User loggedInOperator = m_opmDtc->user(LtsSessionData::LtsSessionData::getInstance()->getOperatorId());
if (c.receiverOperatorId().get() != loggedInOperator.id())
return false;
OperatorManagement::User receiver = m_opmDtc->user(c.receiverOperatorId().get());
strReceiverOperatorName = QString::fromStdString(receiver.name());
}
CommentTuple t;
t.m_commentId = c.id();
t.m_timestamp = QDateTime::fromMSecsSinceEpoch(c.ts());
t.m_sender = strSenderOperatorName;
t.m_receiver = strReceiverOperatorName;
t.m_text = QString::fromStdString(c.text());
t.m_hierarchy = QString::fromStdString(c.hierarchy());
return m_model->insertRow(t);
}
}
+56
View File
@@ -0,0 +1,56 @@
/**
* @RDE_HEADER@
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Last, NLSE, 2018-01-11
*/
#pragma once
#include <LtsWidgets/lts_dialog.h>
#include <Evaluations/logger.h>
#include <Evaluations/evl_dtc.h>
#include <Plugins/OperatorManagement/opm_data_plugin_interface.h>
namespace Ui
{
class EvlCommentsDialog;
}
namespace Evaluations
{
class EvlCommentDataViewModel;
class EvlCommentDataViewProxyModel;
class EvlCommentsView : public LtsWidgets::LtsDialog
{
Q_OBJECT
public:
EvlCommentsView(const std::shared_ptr<Interface::Dtc> &evlDtc,
const std::shared_ptr<OperatorManagement::Interface::Dtc> &opmDtc,
QWidget *parent = nullptr);
~EvlCommentsView();
private slots:
void onLtsDataView_DoubleClicked();
void onCommentChanged(const Evaluations::EvaluationComment &data, DataModification modType);
private:
bool insertComment(const Evaluations::EvaluationComment &c);
void retranslate() override;
bool eventFilter(QObject *object, QEvent *event) override;
void tableColumnOrganiserSetting() override;
Ui::EvlCommentsDialog *m_ui{nullptr};
std::shared_ptr<Interface::Dtc> m_evlDtc;
std::shared_ptr<OperatorManagement::Interface::Dtc> m_opmDtc;
EvlCommentDataViewModel *m_model{nullptr};
EvlCommentDataViewProxyModel *m_proxyModel{nullptr};
NGL::INGLLogger &m_logger;
};
} // namespace BuildingViewer
+45
View File
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EvlCommentsDialog</class>
<widget class="QWidget" name="EvlCommentsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>455</width>
<height>341</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true"/>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="LtsDataView::LtsDataView" name="m_tableComments">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>250</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsDataView::LtsDataView</class>
<extends>QWidget</extends>
<header>LtsDataView/lts_data_view.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
File diff suppressed because it is too large. Load diff
+143
View File
@@ -0,0 +1,143 @@
/**
* @RDE_HEADER@
* $Id$
* $HeadURL$
* $Author$
*
* Created by Philipp Last, NLSE, 02.01.2018
*/
#pragma once
#include <Evaluations/logger.h>
#include <Evaluations/evl_dtc.h>
#include <Evaluations/evl_ftc.h>
#include <OcSituationClient/osc_ftc.h>
#include <FileManagement/flm_dtc.h>
#include <FileManagement/flm_ftc.h>
#include <LtsWidgets/lts_dialog.h>
#include <LtsUtilsGuiDds/request_helper.h>
#include <Plugins/OperatorManagement/opm_data_plugin_interface.h>
#include <Plugins/FileManagement/flm_explorer_gui_plugin_interface.h>
#include <Plugins/FileManagement/Data/flm_data_plugin.h>
#include <LtsSession/lts_session.h>
#include "evl_edit_treemodel_schema.h"
namespace Ui
{
class EditSchemaDialog;
}
namespace Evaluations
{
class EvlNewCommentDialog;
class EditSchemaDialog : public LtsWidgets::LtsDialog
, protected Interface::Ftc::EvlCommentModifyReplyHandler
, protected Interface::Ftc::EvlSchemaModifyReplyHandler
, protected FileManagement::Interface::Ftc::FileContentModifyReplyHandler
, protected OcSituationClient::Interface::Ftc::SendEvaluationReplyHandler
{
Q_OBJECT
public:
explicit EditSchemaDialog(bool ocMode, bool runningOnTablet, LtsSession::LtsSession *session, QWidget *parent = nullptr);
~EditSchemaDialog();
bool init(const std::shared_ptr<OcSituationClient::Interface::Ftc> &ocFtc,
const std::shared_ptr<Interface::Ftc> &evlFtc,
const std::shared_ptr<Interface::Dtc> &evlDtc,
const std::shared_ptr<OperatorManagement::Interface::Dtc> &opmDtc,
const std::shared_ptr<FileManagement::Interface::Ftc> &flmFtc,
const std::shared_ptr<FileManagement::Interface::Dtc> &flmDtc,
const std::shared_ptr<FileManagement::ForeignGuiInterface> &flmGui);
void retranslate() override;
private:
void onSendEvaluationReply(const Core::FunctionReplyObject &replyObject) override final;
void onEvlCommentModifyReply(const Core::FunctionReplyObject &replyObject) override final { m_requestHelper->requestReply(replyObject); }
void onEvlSchemaModifyReply(const Core::FunctionReplyObject &replyObject) override final { m_requestHelper->requestReply(replyObject); }
// FileManagement
void onFileContentModifyReply(const Core::FunctionReplyObject &replyObject) override final { m_requestHelper->requestReply(replyObject);}
private slots:
void onRequestSchema();
void onNotifyAboutSchema(const Evaluations::EvaluationSchema &data, DataModification modType);
void onAddNewComment();
void onCommentAccepted();
void onCommentEditAccepted();
void onSendComment();
void onSendAllComments();
void onTreeViewDoubleClicked();
void onEditComment();
void onModifySelectedEntry();
void onTreeViewModelIndexChanged(const QModelIndex &curr);
void onTriedToDropFolderToElement();
void onDropActionFinished(const QModelIndex &parent);
void onShowContextMenu(const QPoint &position);
bool onSaveToLibrary();
void onLoadFromLibrary();
void onAddElement();
void onAddFolder();
void onAddRootFolder();
void onCreateNewSchema();
void onRemoveSelectedElement();
void DebugonLoadSchema();
void onSendSchema();
private:
void loadLatestSchema();
void handleUnsentComments();
void updateCommentLabel();
void markCommentAsSent(const QPersistentModelIndex &idx);
// Somehow identically...
EvlCommentModifyRequest createEvlCommentModifyRequest(const ItemComment &c) const;
OcSituationClient::SendEvaluationRequest createSendEvaluationRequest(const ItemComment &c) const;
QVector<QPersistentModelIndex> m_vecCommentsInSendState;
bool eventFilter(QObject *object, QEvent *event) override;
void addEntry(bool isFolder, bool isNewRoot = false);
void removeEntry(const QModelIndex &idx);
void removeComment(const QModelIndex &idx);
void sendComment(const QModelIndex &idx);
void createExampleSchema();
void loadSchema(qint32 schemaId);
void saveSchema(bool sendToTa);
bool apply() override;
bool m_ocMode{false};
bool m_runningOnTablet{false};
TreeModelSchema *m_pModel{nullptr};
QMap<qint32, Evaluations::EvaluationSchema> m_mapSchemata;
qint32 m_loadedSchemaId{0};
std::shared_ptr<OcSituationClient::Interface::Ftc> m_ocFtc;
std::shared_ptr<Interface::Dtc> m_evlDtc;
std::shared_ptr<Interface::Ftc> m_evlFtc;
std::shared_ptr<OperatorManagement::Interface::Dtc> m_opmDtc;
std::shared_ptr<FileManagement::Interface::Ftc> m_flmFtc;
std::shared_ptr<FileManagement::Interface::Dtc> m_flmDtc;
std::shared_ptr<FileManagement::ForeignGuiInterface> m_flmGui;
QString m_lastFileName;
Evaluations::EvlNewCommentDialog *m_commentDlg{nullptr};
LtsSession::LtsSession *m_session{nullptr};
LtsUtils::RequestHelper *m_requestHelper{nullptr};
Ui::EditSchemaDialog *m_ui{nullptr};
LOGGER_T m_logger;
};
}
+223
View File
@@ -0,0 +1,223 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EditSchemaDialog</class>
<widget class="QWidget" name="EditSchemaDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1900</width>
<height>515</height>
</rect>
</property>
<property name="windowTitle">
<string>Presentation Editor</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="3">
<widget class="QTreeView" name="m_treeViewSchema">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::InternalMove</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::MoveAction</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="iconSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="expandsOnDoubleClick">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>27</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QPushButton" name="m_pushButton_RequestSchema">
<property name="text">
<string>Request schema</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="m_labelComments">
<property name="text">
<string notr="true">TextLabel</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="m_pushButton_SendComment">
<property name="text">
<string>Send</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="m_pushButton_SendAllComments">
<property name="text">
<string>Send all</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="m_pushButton_NewComment">
<property name="text">
<string>New comment</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="m_pushButton_EditComment">
<property name="text">
<string>Edit comment</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="m_pushButton_RemoveComment">
<property name="text">
<string>Remove comment</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="m_pushButton_CreateNewSchema">
<property name="text">
<string>Create new schema</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="m_pushButton_SendSchema">
<property name="text">
<string>Send schema</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="m_pushButton_LoadSchemaDebug">
<property name="text">
<string>Load schema (DEBUG)</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="m_pushButton_SaveToLibrary">
<property name="text">
<string>Save to library</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="m_pushButton_LoadFromLibrary">
<property name="text">
<string>Load from library</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="m_pushButton_AddRootFolder">
<property name="text">
<string>Add root folder</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="m_pushButton_AddFolder">
<property name="text">
<string>Add folder</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="m_pushButton_AddElement">
<property name="text">
<string>Add element</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="m_pushButton_Modify">
<property name="text">
<string>Modify</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="m_pushButton_Remove">
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,43 @@
/**
* @RDE_HEADER@
* $Id$
* $HeadURL$
* $Author$
*
* Created by Philipp Last, NLSE, 08.01.2018
*/
#include "evl_edit_schema_element_data.h"
namespace Evaluations
{
QDataStream &operator<<(QDataStream &out, const ElementData &e)
{
out << e.m_id;
out << e.m_parentId;
out << e.m_text;
out << int(e.m_state);
out << e.m_icon; // Not working
return out;
}
QDataStream &operator >>(QDataStream &in, ElementData::State &s)
{
int state;
in >> state;
s = static_cast<ElementData::State>(state);
return in;
}
QDataStream &operator>>(QDataStream &in, ElementData &e)
{
in >> e.m_id;
in >> e.m_parentId;
in >> e.m_text;
in >> e.m_state;
in >> e.m_icon; // Not working
return in;
}
} // namespace Evaluations
@@ -0,0 +1,221 @@
/**
* @RDE_HEADER@
* $Id$
* $HeadURL$
* $Author$
*
* Created by Philipp Last, NLSE, 08.01.2018
*/
#pragma once
#include <QIcon>
#include <QString>
#include <QDateTime>
#include <QDataStream>
namespace Evaluations
{
class ItemComment
{
public:
ItemComment()
: m_timeStamp(QDateTime::currentDateTimeUtc())
, m_icon(QIcon(QStringLiteral("icon:messages_disabled.svg")))
{}
ItemComment(const QString &text, int32_t senderId, int32_t receiverId, int32_t elementId, const QString &hierarchy)
: m_elementId(elementId)
, m_senderId(senderId)
, m_receiverId(receiverId)
, m_timeStamp(QDateTime::currentDateTimeUtc())
, m_text(text)
, m_hierarchy(hierarchy)
, m_icon(QIcon(QStringLiteral("icon:messages_disabled.svg")))
{}
ItemComment(const QString &text, int32_t senderId, int32_t receiverId, int32_t elementId, const QDateTime &timeStamp)
: m_elementId(elementId)
, m_senderId(senderId)
, m_receiverId(receiverId)
, m_timeStamp(timeStamp)
, m_text(text)
, m_icon(QIcon(QStringLiteral("icon:messages.svg")))
{}
bool isSent() const
{
return m_isSent;
}
void markAsSent()
{
m_icon = QIcon(QStringLiteral("icon:messages.svg"));
m_isSent = true;
}
const QString &getText() const
{
return m_text;
}
const QString &getHierarchy() const
{
return m_hierarchy;
}
void setText(const QString &strText)
{
m_text = strText;
}
const QDateTime &getTimeStamp() const
{
return m_timeStamp;
}
const QIcon &getIcon() const
{
return m_icon;
}
bool hasReceiverId() const
{
return m_receiverId != 0;
}
int32_t receiverId() const
{
return m_receiverId;
}
int32_t senderId() const
{
return m_senderId;
}
int32_t elementId() const
{
return m_elementId;
}
private:
bool m_isSent{false};
int32_t m_elementId{0}; // Parent id (which is an element)
int32_t m_senderId{0};
int32_t m_receiverId{0};
QDateTime m_timeStamp;
QString m_text;
QString m_hierarchy;
QIcon m_icon;
};
class ElementData
{
public:
enum class State {IsFolder, ElementWithoutComments, ElementWithUnsentComments, ElementAllCommentsSent};
ElementData()
{
refreshIcon();
}
explicit ElementData(const QString &text, bool isFolder = true)
: m_state(isFolder ? State::IsFolder : State::ElementWithoutComments), m_text(text)
{
refreshIcon();
}
int m_id{0};
int m_parentId{0};
void switchState(State newState)
{
m_state = newState;
refreshIcon();
}
bool hasComments() const
{
return (m_state != State::IsFolder) && (m_state != State::ElementWithoutComments);
}
bool hasUnsentComments() const
{
return hasComments() && (m_state != State::ElementAllCommentsSent);
}
bool isFolder() const
{
return m_state == State::IsFolder;
}
QIcon getIcon() const
{
return m_icon;
}
void setText(const QString &strText)
{
m_text = strText;
}
void markAsFolder(bool isFolder)
{
m_state = isFolder ? State::IsFolder : State::ElementWithoutComments;
refreshIcon();
}
QString getText() const
{
return m_text;
}
void refreshIcon()
{
switch (m_state)
{
case State::IsFolder:
m_icon = QIcon(QStringLiteral("icon:evaluation_folder.svg"));
break;
case State::ElementAllCommentsSent:
m_icon = QIcon(QStringLiteral("icon:evaluation_sent_comments.svg"));
break;
case State::ElementWithUnsentComments:
m_icon = QIcon(QStringLiteral("icon:evaluation_unsent_comment.svg"));
break;
case State::ElementWithoutComments:
m_icon = QIcon(QStringLiteral("icon:evaluation_no_comments.svg"));
break;
}
}
State getState() const
{
return m_state;
}
private:
State m_state{State::IsFolder};
QString m_text;
QIcon m_icon;
friend QDataStream &operator<<(QDataStream &out, const ElementData &e);
friend QDataStream &operator>>(QDataStream &in, ElementData &e);
};
QDataStream &operator<<(QDataStream &out, const ElementData &e);
QDataStream &operator>>(QDataStream &in, ElementData &e);
}
Q_DECLARE_METATYPE(Evaluations::ItemComment)
Q_DECLARE_METATYPE(Evaluations::ElementData)
@@ -0,0 +1,92 @@
/**
* @RDE_HEADER@
* $Id$
* $HeadURL$
* $Author$
*
* Created by Philipp Last, NLSE, 05.01.2018
*/
#include "evl_edit_treemodel_general.h"
namespace Evaluations
{
TreeModelGeneral::TreeModelGeneral(QObject *parent, NGL::INGLLogger &logger)
: NGLTreeModel(parent, new NGLTreeItem(QVariant()), {QString()}), m_logger(logger)
{
}
void TreeModelGeneral::iterateModel(const QModelIndex &parent, const std::function<void(const QModelIndex &, const QVariant &)> &fCallback, bool recursive) const
{
for (int r = 0; r < rowCount(parent); ++r)
{
QModelIndex idx = index(r, 0, parent);
QVariant v = TreeModelGeneral::data(idx);
fCallback(idx, v);
if (recursive && hasChildren(idx))
iterateModel(idx, fCallback);
}
}
QVariant TreeModelGeneral::dataAsVariant(const QModelIndex &index) const
{
if (!index.isValid())
return QVariant();
NGLTreeItem *item = privateData(index);
return item->data();
}
QVariant TreeModelGeneral::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return NGLTreeModel::data(index, role);
if (role != Qt::DisplayRole)
return NGLTreeModel::data(index, role);
NGLTreeItem *item = privateData(index);
return item->data();
}
QModelIndex TreeModelGeneral::insertRow(const QModelIndex &parent, const QVariant &data, const QIcon &icon)
{
int row = rowCount(parent);
NGLTreeItem *item = static_cast<NGLTreeItem *>(createItem(data, icon));
item->setModified(true);
if (!NGLTreeModel::insertRow(row, parent, item))
return {};
QModelIndex newIdx = index(row, 0, parent);
return newIdx;
}
bool TreeModelGeneral::updateRow(const QModelIndex &index, const QVariant &data, const QIcon &icon)
{
if (!index.isValid())
return false;
return setItemData(index, data, icon);
}
/*
* All sub elements are also removed.
*/
void TreeModelGeneral::removeRow(const QModelIndex &idx, bool deleteRootAllowed)
{
if (!idx.parent().isValid() && !deleteRootAllowed)
return;
removeRows(idx.row(), 1, idx.parent());
}
} // namespace Evaluations
@@ -0,0 +1,38 @@
/**
* @RDE_HEADER@
* $Id$
* $HeadURL$
* $Author$
*
* Created by Philipp Last, NLSE, 05.01.2018
*/
#pragma once
#include <Evaluations/logger.h>
#include <nglwidgets/ngltreemodel.h>
#include <nglwidgets/ngltreeitem.h>
#include "evl_edit_schema_element_data.h"
namespace Evaluations
{
class TreeModelGeneral : public NGLTreeModel
{
Q_OBJECT
public:
TreeModelGeneral(QObject *parent,
NGL::INGLLogger &logger);
void iterateModel(const QModelIndex &parent, const std::function<void(const QModelIndex &, const QVariant &)> &fCallback, bool recursive = true) const;
QVariant dataAsVariant(const QModelIndex &index) const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QModelIndex insertRow(const QModelIndex &parent, const QVariant &data, const QIcon &icon);
bool updateRow(const QModelIndex &index, const QVariant &data, const QIcon &icon);
void removeRow(const QModelIndex &idx, bool deleteRootAllowed = false);
protected:
LOGGER_T m_logger;
};
}
@@ -0,0 +1,314 @@
/**
* @RDE_HEADER@
* $Id$
* $HeadURL$
* $Author$
*
* Created by Philipp Last, NLSE, 05.01.2018
*/
#include "evl_edit_treemodel_schema.h"
#include <ngltools/ngltools_dtg.h>
#include <QJsonArray>
#include <QJsonObject>
namespace Evaluations
{
TreeModelSchema::TreeModelSchema(QObject *parent, NGL::INGLLogger &logger)
: TreeModelGeneral(parent, logger)
{
qRegisterMetaTypeStreamOperators<Evaluations::ElementData>();
}
void TreeModelSchema::resetModel()
{
TreeModelGeneral::resetModel();
m_elementId = 1;
m_cntComments = 0;
m_cntSentComments = 0;
}
bool TreeModelSchema::dropMimeData(const QMimeData *data, Qt::DropAction action,
int row, int column, const QModelIndex &parent)
{
/*
* Prevent dropping/appending elements to other elements!
* Only dropping to folders is allowed!
*/
ElementData p = getSelectedIndexDataAsElement(parent);
if (!p.isFolder())
{
emit signalTriedToDropFolderToElement();
return false;
}
bool success = TreeModelGeneral::dropMimeData(data, action, row, column, parent);
if (success)
{
/*
* User may have moved a folder with some elements into another folder.
* Hence, we have to update the parent ids.
*/
int parentId = getSelectedIndexDataAsElement(parent).m_id;
iterateModel(parent, [this, parentId](const QModelIndex & idx, const QVariant & v)
{
Evaluations::ElementData e = v.value<Evaluations::ElementData>();
e.m_parentId = parentId;
updateElementData(idx, e);
}, false);
refreshAllIcons();
emit signalSuccessfulDropPerformed(parent);
}
return success;
}
QVariant TreeModelSchema::data(const QModelIndex &index, int role) const
{
QVariant v = TreeModelGeneral::data(index, role);
if (v.isNull())
return v;
if (v.canConvert<Evaluations::ElementData>())
{
Evaluations::ElementData e = v.value<Evaluations::ElementData>();
return e.getText();
}
else if (v.canConvert<Evaluations::ItemComment>())
{
Evaluations::ItemComment c = v.value<Evaluations::ItemComment>();
QString str(DTG::toStringMSec(c.getTimeStamp().toMSecsSinceEpoch()));
str += QLatin1Char(' ');
str += c.getText();
return str;
}
return v;
}
ElementData TreeModelSchema::getSelectedIndexDataAsElement(const QModelIndex &idx) const
{
if (!idx.isValid())
ElementData();
QVariant v = TreeModelGeneral::dataAsVariant(idx);
return v.value<Evaluations::ElementData>();
}
QVector<QString> TreeModelSchema::getSelectedIndexHierarchy(const QModelIndex &idx) const
{
if (!idx.isValid())
QString();
QVector<QString> vecHierarchy;
QModelIndex iterIdx = idx;
while (iterIdx.parent() != QModelIndex())
{
Evaluations::ElementData e = getSelectedIndexDataAsElement(iterIdx);
vecHierarchy.push_front(e.getText());
iterIdx = iterIdx.parent();
}
Evaluations::ElementData e = getSelectedIndexDataAsElement(iterIdx);
vecHierarchy.push_front(e.getText());
return vecHierarchy;
}
QJsonDocument TreeModelSchema::toJsonDocument() const
{
QJsonArray arr;
QModelIndex root;
iterateModel(root, [this, &arr](const QModelIndex & idx, const QVariant & v)
{
Q_UNUSED(idx);
Evaluations::ElementData e = v.value<Evaluations::ElementData>();
INFO_LOG(m_logger) << "toJsonDocument: Name: " << e.getText() << ", elementId: " << e.m_id << ", parent id: " << e.m_parentId;
QJsonObject obj;
obj[QStringLiteral("id")] = e.m_id;
obj[QStringLiteral("parentId")] = e.m_parentId;
obj[QStringLiteral("text")] = e.getText();
obj[QStringLiteral("state")] = int(e.getState());
arr.append(obj);
});
QJsonDocument doc(arr);
return doc;
}
bool TreeModelSchema::fromQJsonDocument(const QJsonDocument &doc)
{
resetModel();
const QJsonArray arr(doc.array());
if (!arr.size())
return false;
QHash<int, QPersistentModelIndex> mapId2Index;
for (const QJsonValue &val : arr)
{
const QJsonObject obj = val.toObject();
ElementData d(obj[QStringLiteral("text")].toString());
d.m_id = obj[QStringLiteral("id")].toInt();
d.m_parentId = obj[QStringLiteral("parentId")].toInt();
d.switchState((ElementData::State)obj[QStringLiteral("state")].toInt());
QPersistentModelIndex parent = (d.m_parentId == 0) ? QPersistentModelIndex() : mapId2Index[d.m_parentId];
QModelIndex newIdx = TreeModelGeneral::insertRow(parent, QVariant::fromValue(d), d.getIcon());
mapId2Index[d.m_id] = newIdx;
/*
* Get current max element id. This is required since
* the user will most likely add new elements to the loaded treeview / element list
*/
m_elementId = std::max(m_elementId, d.m_id);
INFO_LOG(m_logger) << "fromQJsonDocument: " << d.getText() << ", own id = " << d.m_id << ", parent id = " << d.m_parentId;
}
++m_elementId;
return true;
}
QModelIndex TreeModelSchema::insertComment(const QModelIndex &parent, const ItemComment &data)
{
QModelIndex idx = TreeModelGeneral::insertRow(parent, QVariant::fromValue(data), data.getIcon());
if (idx.isValid())
{
++m_cntComments;
if (data.isSent())
++m_cntSentComments;
refreshElementDataIconState(parent);
}
return idx;
}
void TreeModelSchema::insertComment(const ItemComment &data)
{
QModelIndex root = QModelIndex();
iterateModel(root, [&data, this](const QModelIndex & idx, const QVariant & v)
{
if (v.canConvert<Evaluations::ElementData>())
{
ElementData e = v.value<Evaluations::ElementData>();
if (e.m_id == data.elementId())
TreeModelSchema::insertComment(idx, data);
}
});
}
bool TreeModelSchema::updateComment(const QModelIndex &index, const ItemComment &data)
{
bool success = TreeModelGeneral::updateRow(index, QVariant::fromValue(data), data.getIcon());
if (success)
{
if (data.isSent())
++m_cntSentComments;
refreshElementDataIconState(index.parent());
}
return success;
}
void TreeModelSchema::removeComment(const QModelIndex &idx)
{
if (!idx.isValid())
return;
--m_cntComments;
QModelIndex idxParentElement = idx.parent();
removeRow(idx);
refreshElementDataIconState(idxParentElement);
}
QModelIndex TreeModelSchema::insertElementData(const QModelIndex &parent, const ElementData &data)
{
ElementData d(data);
d.m_id = m_elementId++;
d.m_parentId = !parent.isValid() ? 0 : getSelectedIndexDataAsElement(parent).m_id;
return TreeModelGeneral::insertRow(parent, QVariant::fromValue(d), d.getIcon());
}
bool TreeModelSchema::updateElementData(const QModelIndex &index, const ElementData &data)
{
return TreeModelGeneral::updateRow(index, QVariant::fromValue(data), data.getIcon());
}
void TreeModelSchema::refreshElementDataIconState(const QModelIndex &idx)
{
ElementData e = getSelectedIndexDataAsElement(idx);
if (e.isFolder())
return;
if (!rowCount(idx))
e.switchState(ElementData::State::ElementWithoutComments);
else
{
bool hasUnsentComments = false;
iterateModel(idx, [&hasUnsentComments](const QModelIndex & idx, const QVariant & v)
{
Q_UNUSED(idx);
const Evaluations::ItemComment d = v.value<Evaluations::ItemComment>();
if (!d.isSent())
hasUnsentComments = true;
});
e.switchState(hasUnsentComments ? ElementData::State::ElementWithUnsentComments : ElementData::State::ElementAllCommentsSent);
}
TreeModelGeneral::updateRow(idx, QVariant::fromValue(e), e.getIcon());
}
void TreeModelSchema::refreshAllIcons()
{
QModelIndex root;
iterateModel(root, [this](const QModelIndex & idx, const QVariant & v)
{
Evaluations::ElementData d = v.value<Evaluations::ElementData>();
d.refreshIcon();
NGLTreeItem *item = privateData(idx);
item->setIcon(d.getIcon());
});
emit dataChanged(QModelIndex(), QModelIndex());
}
} // namespace Evaluations
@@ -0,0 +1,84 @@
/**
* @RDE_HEADER@
* $Id$
* $HeadURL$
* $Author$
*
* Created by Philipp Last, NLSE, 05.01.2018
*/
#pragma once
#include <Evaluations/logger.h>
#include "evl_edit_treemodel_general.h"
#include <QJsonDocument>
namespace Ui
{
class EditSchemaDialog;
}
namespace Evaluations
{
class TreeModelSchema : public TreeModelGeneral
{
Q_OBJECT
public:
TreeModelSchema(QObject *parent,
NGL::INGLLogger &logger);
void resetModel();
// We can only move elements around but not copy them
Qt::DropActions supportedDropActions() const override { return Qt::MoveAction; }
Qt::DropActions supportedDragActions() const override { return Qt::MoveAction; }
bool dropMimeData(const QMimeData *data, Qt::DropAction action,
int row, int column, const QModelIndex &parent) override;
QJsonDocument toJsonDocument() const;
bool fromQJsonDocument(const QJsonDocument &doc);
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
ElementData getSelectedIndexDataAsElement(const QModelIndex &idx) const;
QVector<QString> getSelectedIndexHierarchy(const QModelIndex &idx) const;
// Comments
unsigned int cntComments() const
{
return m_cntComments;
}
unsigned int cntSentComments() const
{
return m_cntSentComments;
}
QModelIndex insertComment(const QModelIndex &parent, const ItemComment &data);
void insertComment(const ItemComment &data);
bool updateComment(const QModelIndex &index, const ItemComment &data);
void removeComment(const QModelIndex &idx);
// ElementData (Folder or Elements)
QModelIndex insertElementData(const QModelIndex &parent, const ElementData &data);
bool updateElementData(const QModelIndex &index, const ElementData &data);
signals:
void signalTriedToDropFolderToElement();
void signalSuccessfulDropPerformed(const QModelIndex &parent);
private:
void refreshElementDataIconState(const QModelIndex &idx);
void refreshAllIcons();
unsigned int m_cntComments{0};
unsigned int m_cntSentComments{0};
int m_elementId{1};
};
}
@@ -0,0 +1,101 @@
/**
* @RDE_HEADER@
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Last, NLSE, 2018-01-11
*/
#include "ui_evl_new_comment_dialog.h"
#include "evl_new_comment_dialog.h"
#include <Evaluations/logger.h>
#include <LtsUtilsSettings/settings.h>
namespace Evaluations
{
EvlNewCommentDialog::EvlNewCommentDialog(const std::shared_ptr<OperatorManagement::Interface::Dtc> &opmDtc,
QWidget *parent)
: QDialog(parent),
m_ui(new Ui::EvlNewCommentDialog),
m_logger(getLogger("EvlComments::EvlNewCommentDialog"))
{
m_ui->setupUi(this);
setWindowFlags(Qt::Tool);
m_ui->m_ltsComboBox_ReceiverOperator->addItem(tr("All (Default)"), 0);
auto cbUser = [this](const OperatorManagement::User &user)
{
m_ui->m_ltsComboBox_ReceiverOperator->addItem(QString::fromStdString(user.name()), user.id());
};
opmDtc->users(cbUser);
connect(m_ui->m_pushButton_AddComment, &QPushButton::clicked, this, &QDialog::accept);
connect(m_ui->m_pushButton_Cancel, &QPushButton::clicked, this, &QDialog::reject);
connect(m_ui->m_plainTextEdit_Comment, &QPlainTextEdit::textChanged, this, &EvlNewCommentDialog::onTextChanged);
m_ui->m_checkBox_SendComment->setChecked(LtsUtils::OperatorSettings::getInstance()->value(QStringLiteral("Evaluations"), QStringLiteral("setAutoSendComments"), false).toBool());
onTextChanged();
}
EvlNewCommentDialog::~EvlNewCommentDialog()
{
LtsUtils::OperatorSettings::getInstance()->setValue(QStringLiteral("Evaluations"), QStringLiteral("setAutoSendComments"), m_ui->m_checkBox_SendComment->isChecked());
delete m_ui;
}
void EvlNewCommentDialog::onTextChanged()
{
QString plainText(m_ui->m_plainTextEdit_Comment->toPlainText());
int currentUsedChars = plainText.toUtf8().size();
bool bNeedSetNew = false;
while (currentUsedChars > m_maxChars)
{
plainText.chop(1);
currentUsedChars = plainText.toUtf8().size();
bNeedSetNew = true; // do not set text directly, results in recursion
}
// if text longer than allowed
if (bNeedSetNew)
{
m_ui->m_plainTextEdit_Comment->setPlainText(plainText);
m_ui->m_plainTextEdit_Comment->moveCursor(QTextCursor::End);
}
m_ui->m_label_CharsLeft->setText(tr("Characters left: %1").arg(m_maxChars - currentUsedChars));
}
bool EvlNewCommentDialog::autoSend() const
{
return m_ui->m_checkBox_SendComment->isChecked();
}
QString EvlNewCommentDialog::comment() const
{
return m_ui->m_plainTextEdit_Comment->toPlainText();
}
int EvlNewCommentDialog::selectedReceiverId() const
{
return m_ui->m_ltsComboBox_ReceiverOperator->currentData().toInt();
}
void EvlNewCommentDialog::setAsEditComment(const QString &text)
{
m_ui->m_plainTextEdit_Comment->setPlainText(text);
m_ui->m_label_CharsLeft->hide();
m_ui->label->hide();
m_ui->m_ltsComboBox_ReceiverOperator->hide();
m_ui->m_checkBox_SendComment->hide();
m_ui->m_pushButton_AddComment->setText(tr("Edit Comment"));
}
}
@@ -0,0 +1,45 @@
/**
* @RDE_HEADER@
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Last, NLSE, 2018-01-12
*/
#pragma once
#include <LtsWidgets/lts_dialog.h>
#include <Plugins/OperatorManagement/opm_data_plugin_interface.h>
namespace Ui
{
class EvlNewCommentDialog;
}
namespace Evaluations
{
class EvlNewCommentDialog : public QDialog
{
Q_OBJECT
public:
explicit EvlNewCommentDialog(const std::shared_ptr<OperatorManagement::Interface::Dtc> &opmDtc,
QWidget *parent = nullptr);
~EvlNewCommentDialog();
bool autoSend() const;
QString comment() const;
int selectedReceiverId() const;
void setAsEditComment(const QString &text);
private slots:
void onTextChanged();
private:
Ui::EvlNewCommentDialog *m_ui{nullptr};
const int m_maxChars{255};
NGL::INGLLogger &m_logger;
};
} // namespace Evaluations
@@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EvlNewCommentDialog</class>
<widget class="QWidget" name="EvlNewCommentDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>540</width>
<height>347</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true"/>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="3" column="7">
<widget class="QPushButton" name="m_pushButton_Cancel">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="8">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Comment:</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QPlainTextEdit" name="m_plainTextEdit_Comment">
<property name="toolTip">
<string>Comment</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="m_label_CharsLeft">
<property name="text">
<string notr="true">TextLabel</string>
</property>
</widget>
</item>
<item row="1" column="3" colspan="2">
<widget class="QLabel" name="label">
<property name="text">
<string>Receiver operator:</string>
</property>
</widget>
</item>
<item row="3" column="4" colspan="2">
<widget class="QCheckBox" name="m_checkBox_SendComment">
<property name="text">
<string>Send comment</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="8">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="3" column="0" colspan="4">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>240</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="6">
<widget class="QPushButton" name="m_pushButton_AddComment">
<property name="text">
<string>Add comment</string>
</property>
</widget>
</item>
<item row="1" column="5" colspan="3">
<widget class="LtsWidgets::LtsComboBox" name="m_ltsComboBox_ReceiverOperator"/>
</item>
<item row="1" column="2">
<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>
</widget>
<customwidgets>
<customwidget>
<class>LtsWidgets::LtsComboBox</class>
<extends>QComboBox</extends>
<header>LtsWidgets/lts_combo_box.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>