Work on the graphical interface for presenting requirements details
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <dbus/api/api.h>
|
#include <dbus/api/api.h>
|
||||||
|
#include <dbus/dBus.h>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
namespace api::project
|
namespace api::project
|
||||||
@@ -18,8 +19,6 @@ enum class OperationType : uint8_t
|
|||||||
|
|
||||||
/* --- */
|
/* --- */
|
||||||
|
|
||||||
//--------------------------------------------------------------
|
|
||||||
// Post event structs
|
|
||||||
//--------------------------------------------------------------
|
//--------------------------------------------------------------
|
||||||
struct ProjectOperationEvent : dBus::api::DefaultData<dBus::makeID("project.file.operation")>
|
struct ProjectOperationEvent : dBus::api::DefaultData<dBus::makeID("project.file.operation")>
|
||||||
{
|
{
|
||||||
@@ -62,12 +61,6 @@ struct ProjectOperationEvent : dBus::api::DefaultData<dBus::makeID("project.file
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
//--------------------------------------------------------------
|
//--------------------------------------------------------------
|
||||||
|
|
||||||
/* --- */
|
|
||||||
|
|
||||||
//--------------------------------------------------------------
|
|
||||||
// Post event helpers
|
|
||||||
//--------------------------------------------------------------
|
|
||||||
inline dBus::api::PostReturnStatus postProjectOperationRequest(dBus::Bus &bus, const OperationType &operationType, const std::filesystem::path &filePath = "")
|
inline dBus::api::PostReturnStatus postProjectOperationRequest(dBus::Bus &bus, const OperationType &operationType, const std::filesystem::path &filePath = "")
|
||||||
{
|
{
|
||||||
auto eventData = ProjectOperationEvent();
|
auto eventData = ProjectOperationEvent();
|
||||||
@@ -77,4 +70,5 @@ inline dBus::api::PostReturnStatus postProjectOperationRequest(dBus::Bus &bus, c
|
|||||||
return dBus::api::requestData<decltype(eventData), void>(bus, eventData.getID(), eventData, dBus::MessageCategory::UI);
|
return dBus::api::requestData<decltype(eventData), void>(bus, eventData.getID(), eventData, dBus::MessageCategory::UI);
|
||||||
}
|
}
|
||||||
//--------------------------------------------------------------
|
//--------------------------------------------------------------
|
||||||
|
using ProjectOperationMessage_ptr = std::shared_ptr<dBus::RequestMessage<ProjectOperationEvent, void>>;
|
||||||
} // namespace api::project
|
} // namespace api::project
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <dbus/api/api.h>
|
#include <dbus/api/api.h>
|
||||||
|
#include <dbus/dBus.h>
|
||||||
|
|
||||||
namespace api::requirement
|
namespace api::requirement
|
||||||
{
|
{
|
||||||
@@ -27,7 +28,7 @@ struct Metadata
|
|||||||
//--------------------------------------------------------------
|
//--------------------------------------------------------------
|
||||||
struct Details
|
struct Details
|
||||||
{
|
{
|
||||||
std::string name; // Short name or title of the requirement
|
std::string title; // Short name or title of the requirement
|
||||||
std::string description; // Detailed description of the requirement
|
std::string description; // Detailed description of the requirement
|
||||||
std::string acceptance_criteria; // Criteria that must be met for the requirement to be considered complete
|
std::string acceptance_criteria; // Criteria that must be met for the requirement to be considered complete
|
||||||
bool is_smart; // SMART indicator (Specific, Measurable, Achievable, Relevant, Time-bound)
|
bool is_smart; // SMART indicator (Specific, Measurable, Achievable, Relevant, Time-bound)
|
||||||
@@ -52,42 +53,146 @@ struct Requirement
|
|||||||
/* --- */
|
/* --- */
|
||||||
|
|
||||||
//--------------------------------------------------------------
|
//--------------------------------------------------------------
|
||||||
// Post event structs
|
struct GetRequirementEvent : dBus::api::DefaultData<dBus::makeID("requirement.get")>
|
||||||
//--------------------------------------------------------------
|
|
||||||
struct RequirementEvent : dBus::api::DefaultData<dBus::makeID("requirement.requirementEvent")>
|
|
||||||
{
|
{
|
||||||
virtual ~RequirementEvent() = default; // Default destructor
|
virtual ~GetRequirementEvent() = default; // Default destructor
|
||||||
|
|
||||||
Requirement requirementData;
|
std::string uuid; // UUID of the requirement (empty for root-level requirements)
|
||||||
|
|
||||||
[[nodiscard]] std::string toString() const override
|
[[nodiscard]] std::string toString() const override
|
||||||
{
|
{
|
||||||
// return std::format("RequirementEvent: id={}, name={}, subRequirementsCount={}",
|
return std::format("GetRequirementEvent: uuid={}", uuid);
|
||||||
// requirementData.id,
|
|
||||||
// requirementData.name,
|
|
||||||
// requirementData.description);
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
//--------------------------------------------------------------
|
//--------------------------------------------------------------
|
||||||
|
inline std::expected<Requirement, dBus::ReturnStatus> postGetRequirementRequest(dBus::Bus &bus, const std::string &uuid)
|
||||||
|
{
|
||||||
|
auto eventData = GetRequirementEvent();
|
||||||
|
eventData.uuid = std::move(uuid);
|
||||||
|
return dBus::api::requestData<decltype(eventData), Requirement>(bus, eventData.getID(), eventData, dBus::MessageCategory::UI);
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
using GetRequirementMessage_ptr = std::shared_ptr<dBus::RequestMessage<GetRequirementEvent, Requirement>>;
|
||||||
|
|
||||||
/* --- */
|
/* --- */
|
||||||
|
|
||||||
//--------------------------------------------------------------
|
//--------------------------------------------------------------
|
||||||
// Post event helpers
|
struct GetChildrenRequirementEvent : dBus::api::DefaultData<dBus::makeID("requirement.getChildren")>
|
||||||
|
{
|
||||||
|
virtual ~GetChildrenRequirementEvent() = default; // Default destructor
|
||||||
|
|
||||||
|
std::string uuid; // UUID of the requirement (empty for root-level requirements)
|
||||||
|
|
||||||
|
[[nodiscard]] std::string toString() const override
|
||||||
|
{
|
||||||
|
return std::format("GetChildrenRequirementEvent: uuid={}", uuid);
|
||||||
|
}
|
||||||
|
};
|
||||||
//--------------------------------------------------------------
|
//--------------------------------------------------------------
|
||||||
|
inline std::expected<std::vector<Requirement>, dBus::ReturnStatus> postGetChildrenRequirementRequest(dBus::Bus &bus, const std::string &uuid)
|
||||||
|
{
|
||||||
|
auto eventData = GetChildrenRequirementEvent();
|
||||||
|
eventData.uuid = std::move(uuid);
|
||||||
|
return dBus::api::requestData<decltype(eventData), std::vector<Requirement>>(bus, eventData.getID(), eventData, dBus::MessageCategory::UI);
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
using GetChildrenRequirementMessage_ptr = std::shared_ptr<dBus::RequestMessage<GetChildrenRequirementEvent, std::vector<Requirement>>>;
|
||||||
|
|
||||||
|
/* --- */
|
||||||
|
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
struct AddRequirementEvent : dBus::api::DefaultData<dBus::makeID("requirement.add")>
|
||||||
|
{
|
||||||
|
virtual ~AddRequirementEvent() = default; // Default destructor
|
||||||
|
|
||||||
|
std::string parentUuid; // UUID of the parent requirement (empty for root-level requirements)
|
||||||
|
Requirement requirementData; // Data of the requirement to be added
|
||||||
|
|
||||||
|
[[nodiscard]] std::string toString() const override
|
||||||
|
{
|
||||||
|
return std::format("AddRequirementEvent: parentUuid={}, id={}, title={}",
|
||||||
|
parentUuid,
|
||||||
|
requirementData.metadata.id,
|
||||||
|
requirementData.details.title);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
inline dBus::api::PostReturnStatus postAddRequirementRequest(dBus::Bus &bus, const std::string &parentUuid, const Requirement &requirement)
|
||||||
|
{
|
||||||
|
auto eventData = AddRequirementEvent();
|
||||||
|
eventData.parentUuid = std::move(parentUuid);
|
||||||
|
eventData.requirementData = std::move(requirement);
|
||||||
|
return dBus::api::requestData<decltype(eventData), void>(bus, eventData.getID(), eventData, dBus::MessageCategory::UI);
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
using AddRequirementMessage_ptr = std::shared_ptr<dBus::RequestMessage<AddRequirementEvent, void>>;
|
||||||
|
|
||||||
|
/* --- */
|
||||||
|
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
struct UpdateRequirementEvent : dBus::api::DefaultData<dBus::makeID("requirement.update")>
|
||||||
|
{
|
||||||
|
virtual ~UpdateRequirementEvent() = default; // Default destructor
|
||||||
|
|
||||||
|
Requirement requirementData; // Data of the requirement to be updated
|
||||||
|
|
||||||
|
[[nodiscard]] std::string toString() const override
|
||||||
|
{
|
||||||
|
return std::format("UpdateRequirementEvent: id={}, title={}",
|
||||||
|
requirementData.metadata.id,
|
||||||
|
requirementData.details.title);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
inline dBus::api::PostReturnStatus postUpdateRequirementRequest(dBus::Bus &bus, const Requirement &requirement)
|
||||||
|
{
|
||||||
|
auto eventData = UpdateRequirementEvent();
|
||||||
|
eventData.requirementData = std::move(requirement);
|
||||||
|
return dBus::api::requestData<decltype(eventData), void>(bus, eventData.getID(), eventData, dBus::MessageCategory::UI);
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
using UpdateRequirementMessage_ptr = std::shared_ptr<dBus::RequestMessage<UpdateRequirementEvent, void>>;
|
||||||
|
|
||||||
|
/* --- */
|
||||||
|
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
struct DeleteRequirementEvent : dBus::api::DefaultData<dBus::makeID("requirement.delete")>
|
||||||
|
{
|
||||||
|
virtual ~DeleteRequirementEvent() = default; // Default destructor
|
||||||
|
|
||||||
|
Requirement requirementData; // Data of the requirement to be deleted
|
||||||
|
|
||||||
|
[[nodiscard]] std::string toString() const override
|
||||||
|
{
|
||||||
|
return std::format("DeleteRequirementEvent: id={}, title={}",
|
||||||
|
requirementData.metadata.id,
|
||||||
|
requirementData.details.title);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
inline dBus::api::PostReturnStatus postDeleteRequirementRequest(dBus::Bus &bus, const Requirement &requirement)
|
||||||
|
{
|
||||||
|
auto eventData = DeleteRequirementEvent();
|
||||||
|
eventData.requirementData = std::move(requirement);
|
||||||
|
return dBus::api::requestData<decltype(eventData), void>(bus, eventData.getID(), eventData, dBus::MessageCategory::UI);
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
using DeleteRequirementMessage_ptr = std::shared_ptr<dBus::RequestMessage<DeleteRequirementEvent, void>>;
|
||||||
|
|
||||||
|
/* --- */
|
||||||
|
|
||||||
/* Post a requirement event to the bus. This function will post a
|
/* Post a requirement event to the bus. This function will post a
|
||||||
* RequirementEvent containing the provided requirement data to the
|
* RequirementEvent containing the provided requirement data to the
|
||||||
* bus, and return a PostReturnStatus indicating whether the post was
|
* bus, and return a PostReturnStatus indicating whether the post was
|
||||||
* successful or if there were any issues (e.g., no subscribers,
|
* successful or if there were any issues (e.g., no subscribers,
|
||||||
* timeout, etc.). */
|
* timeout, etc.). */
|
||||||
inline dBus::api::PostReturnStatus postRequirementRequest(dBus::Bus &bus,
|
// inline dBus::api::PostReturnStatus postRequirementRequest(dBus::Bus &bus,
|
||||||
Requirement requirement)
|
// Requirement requirement)
|
||||||
{
|
//{
|
||||||
auto eventData = RequirementEvent();
|
// auto eventData = RequirementEvent();
|
||||||
eventData.requirementData = std::move(requirement);
|
// eventData.requirementData = std::move(requirement);
|
||||||
|
//
|
||||||
return dBus::api::requestData<decltype(eventData), void>(bus, eventData.getID(), eventData, dBus::MessageCategory::UI);
|
// return dBus::api::requestData<decltype(eventData), void>(bus, eventData.getID(), eventData, dBus::MessageCategory::UI);
|
||||||
}
|
// }
|
||||||
//--------------------------------------------------------------
|
//--------------------------------------------------------------
|
||||||
} // namespace api::requirement
|
} // namespace api::requirement
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "RequirementManager.h"
|
#include "requirementManager.h"
|
||||||
|
|
||||||
#include "requirementItem.h"
|
#include "requirementItem.h"
|
||||||
|
|
||||||
|
|||||||
@@ -35,8 +35,6 @@ class RequirementManager
|
|||||||
protected:
|
protected:
|
||||||
RequirementItemPtr m_rootRequirement; // Root requirement of the current project
|
RequirementItemPtr m_rootRequirement; // Root requirement of the current project
|
||||||
std::unordered_map<std::string, RequirementItemPtr> m_requirementsByUuid; // Map of requirement UUID to requirement pointer for quick lookup
|
std::unordered_map<std::string, RequirementItemPtr> m_requirementsByUuid; // Map of requirement UUID to requirement pointer for quick lookup
|
||||||
|
|
||||||
private:
|
|
||||||
};
|
};
|
||||||
//--------------------------------------------------------------
|
//--------------------------------------------------------------
|
||||||
} // namespace core::project
|
} // namespace core::project
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "projectManager.h"
|
#include "projectManager.h"
|
||||||
|
|
||||||
#include "RequirementManager.h"
|
#include "requirementItem.h"
|
||||||
|
#include "requirementManager.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace core::project;
|
using namespace core::project;
|
||||||
@@ -33,8 +34,13 @@ void ProjectManager::saveFile(const std::filesystem::path &filePath)
|
|||||||
/* Run the bus listener thread */
|
/* Run the bus listener thread */
|
||||||
void ProjectManager::runBusListener()
|
void ProjectManager::runBusListener()
|
||||||
{
|
{
|
||||||
// Subscribe to the bus for messages related to log entries
|
// Subscribe to the bus for messages related to project file and requirement operations
|
||||||
subscribe(dBus::makeID("project.file.operation"));
|
subscribe(dBus::makeID("project.file.operation"));
|
||||||
|
subscribe(dBus::makeID("requirement.get"));
|
||||||
|
subscribe(dBus::makeID("requirement.requirement.getChildren"));
|
||||||
|
subscribe(dBus::makeID("requirement.add"));
|
||||||
|
subscribe(dBus::makeID("requirement.update"));
|
||||||
|
subscribe(dBus::makeID("requirement.delete"));
|
||||||
|
|
||||||
/* Start the bus listener thread to process incoming messages related to requirements.
|
/* Start the bus listener thread to process incoming messages related to requirements.
|
||||||
* The thread will continuously wait for messages and process them until a stop is requested. */
|
* The thread will continuously wait for messages and process them until a stop is requested. */
|
||||||
@@ -79,43 +85,75 @@ void ProjectManager::processMessageBus(const Message_ptr &message)
|
|||||||
const auto messageType = message->getMessageTypeID();
|
const auto messageType = message->getMessageTypeID();
|
||||||
switch (messageType)
|
switch (messageType)
|
||||||
{
|
{
|
||||||
|
// Process project events
|
||||||
case dBus::makeID("project.file.operation"):
|
case dBus::makeID("project.file.operation"):
|
||||||
{
|
{
|
||||||
// Process project operation request
|
on_projectOperationEvent(message);
|
||||||
on_projectOperationEvent(message->as<dBus::RequestMessage<api::project::ProjectOperationEvent, void>>());
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Process requirement events
|
||||||
|
case dBus::makeID("requirement.get"):
|
||||||
|
{
|
||||||
|
on_requirementGetEvent(message->as<dBus::RequestMessage<api::requirement::GetRequirementEvent, api::requirement::Requirement>>());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case dBus::makeID("requirement.getChildren"):
|
||||||
|
{
|
||||||
|
on_requirementGetChildrenEvent(message->as<dBus::RequestMessage<api::requirement::GetChildrenRequirementEvent, std::vector<api::requirement::Requirement>>>());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case dBus::makeID("requirement.add"):
|
||||||
|
{
|
||||||
|
on_requirementAddEvent(message->as<dBus::RequestMessage<api::requirement::AddRequirementEvent, void>>());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case dBus::makeID("requirement.update"):
|
||||||
|
{
|
||||||
|
on_requirementUpdateEvent(message->as<dBus::RequestMessage<api::requirement::UpdateRequirementEvent, void>>());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case dBus::makeID("requirement.delete"):
|
||||||
|
{
|
||||||
|
on_requirementDeleteEvent(message->as<dBus::RequestMessage<api::requirement::DeleteRequirementEvent, void>>());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//--------------------------------------------------------------
|
//--------------------------------------------------------------
|
||||||
/* Process a file operation event */
|
/* Process a file operation event */
|
||||||
void ProjectManager::on_projectOperationEvent(const ProjectOperationMessage_ptr &message)
|
void ProjectManager::on_projectOperationEvent(const Message_ptr &message)
|
||||||
{
|
{
|
||||||
|
const auto specializedMessage = message->as<dBus::RequestMessage<api::project::ProjectOperationEvent, void>>();
|
||||||
|
|
||||||
// Sanity check
|
// Sanity check
|
||||||
if (!message)
|
if (!specializedMessage)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
switch (message->value.operationType)
|
switch (specializedMessage->value.operationType)
|
||||||
{
|
{
|
||||||
using enum api::project::OperationType;
|
using enum api::project::OperationType;
|
||||||
case Create:
|
case Create:
|
||||||
on_projectCreateFileEvent(message);
|
on_projectCreateFileEvent(specializedMessage);
|
||||||
break;
|
break;
|
||||||
case Open:
|
case Open:
|
||||||
on_projectOpenFileEvent(message);
|
on_projectOpenFileEvent(specializedMessage);
|
||||||
break;
|
break;
|
||||||
case Save:
|
case Save:
|
||||||
case SaveAs:
|
case SaveAs:
|
||||||
on_projectSaveFileEvent(message);
|
on_projectSaveFileEvent(specializedMessage);
|
||||||
break;
|
break;
|
||||||
case Close:
|
case Close:
|
||||||
on_projectCloseFileEvent(message);
|
on_projectCloseFileEvent(specializedMessage);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//--------------------------------------------------------------
|
//--------------------------------------------------------------
|
||||||
/* Process a file creation event */
|
/* Process a file creation event */
|
||||||
void ProjectManager::on_projectCreateFileEvent(const ProjectOperationMessage_ptr &message)
|
void ProjectManager::on_projectCreateFileEvent(const api::project::ProjectOperationMessage_ptr &message)
|
||||||
{
|
{
|
||||||
m_requirementManager = make_shared<RequirementManager>();
|
m_requirementManager = make_shared<RequirementManager>();
|
||||||
|
|
||||||
@@ -123,22 +161,22 @@ void ProjectManager::on_projectCreateFileEvent(const ProjectOperationMessage_ptr
|
|||||||
}
|
}
|
||||||
//--------------------------------------------------------------
|
//--------------------------------------------------------------
|
||||||
/* Process a file open event */
|
/* Process a file open event */
|
||||||
void ProjectManager::on_projectOpenFileEvent(const ProjectOperationMessage_ptr &message)
|
void ProjectManager::on_projectOpenFileEvent(const api::project::ProjectOperationMessage_ptr &message)
|
||||||
{
|
{
|
||||||
// message->responsePromise.set_exception(std::make_exception_ptr(std::runtime_error("Open operation not implemented")));
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
openFile(message->value.filePath);
|
openFile(message->value.filePath);
|
||||||
|
|
||||||
|
message->responsePromise.set_value();
|
||||||
}
|
}
|
||||||
catch (const std::exception &e)
|
catch (const std::exception &e)
|
||||||
{
|
{
|
||||||
message->responsePromise.set_exception(std::make_exception_ptr(e));
|
message->responsePromise.set_exception(std::make_exception_ptr(e));
|
||||||
}
|
}
|
||||||
message->responsePromise.set_value();
|
|
||||||
}
|
}
|
||||||
//--------------------------------------------------------------
|
//--------------------------------------------------------------
|
||||||
/* Process a file save event */
|
/* Process a file save event */
|
||||||
void ProjectManager::on_projectSaveFileEvent(const ProjectOperationMessage_ptr &message)
|
void ProjectManager::on_projectSaveFileEvent(const api::project::ProjectOperationMessage_ptr &message)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -154,18 +192,176 @@ void ProjectManager::on_projectSaveFileEvent(const ProjectOperationMessage_ptr &
|
|||||||
throw std::runtime_error("File path is empty for Save operation");
|
throw std::runtime_error("File path is empty for Save operation");
|
||||||
saveFile();
|
saveFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message->responsePromise.set_value();
|
||||||
}
|
}
|
||||||
catch (const std::exception &e)
|
catch (const std::exception &e)
|
||||||
{
|
{
|
||||||
message->responsePromise.set_exception(std::make_exception_ptr(e));
|
message->responsePromise.set_exception(std::make_exception_ptr(e));
|
||||||
}
|
}
|
||||||
message->responsePromise.set_value();
|
|
||||||
}
|
}
|
||||||
//--------------------------------------------------------------
|
//--------------------------------------------------------------
|
||||||
/* Process a file close event */
|
/* Process a file close event */
|
||||||
void ProjectManager::on_projectCloseFileEvent(const ProjectOperationMessage_ptr &message)
|
void ProjectManager::on_projectCloseFileEvent(const api::project::ProjectOperationMessage_ptr &message)
|
||||||
{
|
{
|
||||||
m_requirementManager = {};
|
m_requirementManager = {};
|
||||||
message->responsePromise.set_value();
|
message->responsePromise.set_value();
|
||||||
}
|
}
|
||||||
//--------------------------------------------------------------
|
//--------------------------------------------------------------
|
||||||
|
/* Process a requirement get event */
|
||||||
|
void ProjectManager::on_requirementGetEvent(const api::requirement::GetRequirementMessage_ptr &message) const
|
||||||
|
{
|
||||||
|
// Sanity check
|
||||||
|
if (!message)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Sanity check
|
||||||
|
if (!m_requirementManager)
|
||||||
|
throw std::runtime_error("No project file is currently opened");
|
||||||
|
|
||||||
|
// Get requirement handle
|
||||||
|
const auto requirement = m_requirementManager->getRequirement(message->value.uuid);
|
||||||
|
if (!requirement)
|
||||||
|
throw std::runtime_error("Requirement not found");
|
||||||
|
|
||||||
|
message->responsePromise.set_value(requirement->toRequirement());
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
message->responsePromise.set_exception(std::make_exception_ptr(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
/* Process a requirement get children event */
|
||||||
|
void ProjectManager::on_requirementGetChildrenEvent(const api::requirement::GetChildrenRequirementMessage_ptr &message) const
|
||||||
|
{
|
||||||
|
// Sanity check
|
||||||
|
if (!message)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Sanity check
|
||||||
|
if (!m_requirementManager)
|
||||||
|
throw std::runtime_error("No project file is currently opened");
|
||||||
|
|
||||||
|
// Get requirement handle
|
||||||
|
const auto requirement = m_requirementManager->getRequirement(message->value.uuid);
|
||||||
|
if (!requirement)
|
||||||
|
throw std::runtime_error("Requirement not found");
|
||||||
|
|
||||||
|
// Get children requirements
|
||||||
|
const auto &children = requirement->getChildren();
|
||||||
|
vector<api::requirement::Requirement> childrenData;
|
||||||
|
childrenData.reserve(children.size());
|
||||||
|
for (const auto &child : children)
|
||||||
|
childrenData.push_back(child->toRequirement());
|
||||||
|
|
||||||
|
message->responsePromise.set_value(childrenData);
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
message->responsePromise.set_exception(std::make_exception_ptr(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
/* Process a requirement addition event */
|
||||||
|
void ProjectManager::on_requirementAddEvent(const api::requirement::AddRequirementMessage_ptr &message) const
|
||||||
|
{
|
||||||
|
// Sanity check
|
||||||
|
if (!message)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Sanity check
|
||||||
|
if (!m_requirementManager)
|
||||||
|
throw std::runtime_error("No project file is currently opened");
|
||||||
|
|
||||||
|
// Get parent requirement handle
|
||||||
|
const auto parentRequirement = message->value.parentUuid.empty()
|
||||||
|
? m_requirementManager->getRootRequirement()
|
||||||
|
: m_requirementManager->getRequirement(message->value.parentUuid);
|
||||||
|
if (!parentRequirement)
|
||||||
|
throw std::runtime_error("Parent requirement not found");
|
||||||
|
|
||||||
|
// Append the new requirement to the parent requirement
|
||||||
|
parentRequirement->appendChild(message->value.requirementData);
|
||||||
|
|
||||||
|
message->responsePromise.set_value();
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
message->responsePromise.set_exception(std::make_exception_ptr(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
/* Process a requirement update event */
|
||||||
|
void ProjectManager::on_requirementUpdateEvent(const api::requirement::UpdateRequirementMessage_ptr &message) const
|
||||||
|
{
|
||||||
|
// Sanity check
|
||||||
|
if (!message)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Sanity check
|
||||||
|
if (!m_requirementManager)
|
||||||
|
throw std::runtime_error("No project file is currently opened");
|
||||||
|
|
||||||
|
// Get requirement handle
|
||||||
|
const auto requirement = m_requirementManager->getRequirement(message->value.requirementData.metadata.uuid);
|
||||||
|
if (!requirement)
|
||||||
|
throw std::runtime_error("Requirement not found");
|
||||||
|
|
||||||
|
// Update the requirement data
|
||||||
|
requirement->update(message->value.requirementData);
|
||||||
|
|
||||||
|
message->responsePromise.set_value();
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
message->responsePromise.set_exception(std::make_exception_ptr(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
/* Process a requirement deletion event */
|
||||||
|
void ProjectManager::on_requirementDeleteEvent(const api::requirement::DeleteRequirementMessage_ptr &message) const
|
||||||
|
{
|
||||||
|
// Sanity check
|
||||||
|
if (!message)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Sanity check
|
||||||
|
if (!m_requirementManager)
|
||||||
|
throw std::runtime_error("No project file is currently opened");
|
||||||
|
|
||||||
|
// Get requirement handle
|
||||||
|
const auto requirement = m_requirementManager->getRequirement(message->value.requirementData.metadata.uuid);
|
||||||
|
if (!requirement)
|
||||||
|
throw std::runtime_error("Requirement not found");
|
||||||
|
|
||||||
|
// Get parent requirement handle
|
||||||
|
const auto parentRequirement = requirement->getParent();
|
||||||
|
if (!parentRequirement)
|
||||||
|
{
|
||||||
|
// If the requirement to be deleted is the root requirement,
|
||||||
|
// we cannot delete it as it is the base of the requirements hierarchy
|
||||||
|
throw std::runtime_error("Cannot delete root requirement");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the requirement from its parent
|
||||||
|
parentRequirement->removeChild(requirement->metadata.uuid);
|
||||||
|
|
||||||
|
message->responsePromise.set_value();
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
message->responsePromise.set_exception(std::make_exception_ptr(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
|||||||
@@ -41,12 +41,16 @@ class ProjectManager : public dBus::Node
|
|||||||
std::jthread m_busListenerThread; // Thread for listening to bus messages related to requirements
|
std::jthread m_busListenerThread; // Thread for listening to bus messages related to requirements
|
||||||
|
|
||||||
// Events processing
|
// Events processing
|
||||||
using ProjectOperationMessage_ptr = std::shared_ptr<dBus::RequestMessage<api::project::ProjectOperationEvent, void>>;
|
void on_projectOperationEvent(const Message_ptr &message); // Process a file operation event
|
||||||
void on_projectOperationEvent(const ProjectOperationMessage_ptr &message); // Process a file operation event
|
void on_projectCreateFileEvent(const api::project::ProjectOperationMessage_ptr &message); // Process a file creation event
|
||||||
void on_projectCreateFileEvent(const ProjectOperationMessage_ptr &message); // Process a file creation event
|
void on_projectOpenFileEvent(const api::project::ProjectOperationMessage_ptr &message); // Process a file opening event
|
||||||
void on_projectOpenFileEvent(const ProjectOperationMessage_ptr &message); // Process a file opening event
|
void on_projectSaveFileEvent(const api::project::ProjectOperationMessage_ptr &message); // Process a file saving event
|
||||||
void on_projectSaveFileEvent(const ProjectOperationMessage_ptr &message); // Process a file saving event
|
void on_projectCloseFileEvent(const api::project::ProjectOperationMessage_ptr &message); // Process a file closing event
|
||||||
void on_projectCloseFileEvent(const ProjectOperationMessage_ptr &message); // Process a file closing event
|
void on_requirementGetEvent(const api::requirement::GetRequirementMessage_ptr &message) const; // Process a requirement get event
|
||||||
|
void on_requirementGetChildrenEvent(const api::requirement::GetChildrenRequirementMessage_ptr &message) const; // Process a requirement get children event
|
||||||
|
void on_requirementAddEvent(const api::requirement::AddRequirementMessage_ptr &message) const; // Process a requirement addition event
|
||||||
|
void on_requirementUpdateEvent(const api::requirement::UpdateRequirementMessage_ptr &message) const; // Process a requirement update event
|
||||||
|
void on_requirementDeleteEvent(const api::requirement::DeleteRequirementMessage_ptr &message) const; // Process a requirement deletion event
|
||||||
};
|
};
|
||||||
//--------------------------------------------------------------
|
//--------------------------------------------------------------
|
||||||
} // namespace core::project
|
} // namespace core::project
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#include "requirementItem.h"
|
#include "requirementItem.h"
|
||||||
|
|
||||||
#include "RequirementManager.h"
|
#include "requirementManager.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace core::project;
|
using namespace core::project;
|
||||||
@@ -17,21 +17,45 @@ RequirementItem::RequirementItem(RequirementManager &manager, const std::shared_
|
|||||||
m_requirementManager.registerRequirement(shared_from_this());
|
m_requirementManager.registerRequirement(shared_from_this());
|
||||||
}
|
}
|
||||||
//--------------------------------------------------------------
|
//--------------------------------------------------------------
|
||||||
|
/* Convert this requirement item to an api::requirement::Requirement struct and return it */
|
||||||
|
api::requirement::Requirement RequirementItem::toRequirement() const
|
||||||
|
{
|
||||||
|
return { .metadata = metadata,
|
||||||
|
.details = details,
|
||||||
|
.classification = classification };
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
/* Update the data of this requirement item with the provided updated requirement data */
|
||||||
|
void RequirementItem::update(const api::requirement::Requirement &updatedData)
|
||||||
|
{
|
||||||
|
const auto currentUUID = metadata.uuid; // Store the current UUID before updating
|
||||||
|
|
||||||
|
// Update the requirement data with the provided updated requirement data
|
||||||
|
metadata = updatedData.metadata;
|
||||||
|
details = updatedData.details;
|
||||||
|
classification = updatedData.classification;
|
||||||
|
|
||||||
|
// Restore the original UUID to maintain consistency
|
||||||
|
metadata.uuid = currentUUID;
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
/* Get the m_parent requirement item of this requirement item (nullptr for root) */
|
||||||
|
RequirementItem::RequirementItemPtr RequirementItem::getParent() const
|
||||||
|
{
|
||||||
|
// Return the m_parent requirement item pointer if it exists
|
||||||
|
// or nullptr if this requirement item is the root
|
||||||
|
return m_parent.lock();
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------
|
||||||
/* Append a child requirement item to this requirement item */
|
/* Append a child requirement item to this requirement item */
|
||||||
void RequirementItem::appendChild(const api::requirement::Requirement &child)
|
void RequirementItem::appendChild(const Requirement &child)
|
||||||
{
|
{
|
||||||
// Create a new requirement item for the child requirement and set this requirement item as its m_parent
|
// Create a new requirement item for the child requirement and set this requirement item as its m_parent
|
||||||
const auto childItem = make_shared<RequirementItem>(m_requirementManager);
|
const auto childItem = make_shared<RequirementItem>(m_requirementManager, shared_from_this());
|
||||||
childItem->metadata = child.metadata;
|
childItem->update(child); // Update the child requirement item with the provided child requirement data
|
||||||
childItem->details = child.details;
|
|
||||||
childItem->classification = child.classification;
|
|
||||||
|
|
||||||
// Prepare requirement links
|
// Append the child requirement item to the vector of children
|
||||||
childItem->metadata.uuid = m_requirementManager.generateUniqueUuid(); // Generate a unique UUID for this requirement item
|
m_children.push_back(childItem);
|
||||||
childItem->m_parent = shared_from_this();
|
|
||||||
|
|
||||||
// Register this requirement item in the manager's internal map
|
|
||||||
m_requirementManager.registerRequirement(childItem);
|
|
||||||
}
|
}
|
||||||
//--------------------------------------------------------------
|
//--------------------------------------------------------------
|
||||||
/* Remove a child requirement item from this requirement item by its UUID */
|
/* Remove a child requirement item from this requirement item by its UUID */
|
||||||
@@ -41,11 +65,24 @@ void RequirementItem::removeChild(const std::string &childUuid)
|
|||||||
m_requirementManager.unregisterRequirement(childUuid);
|
m_requirementManager.unregisterRequirement(childUuid);
|
||||||
|
|
||||||
// Find the child requirement item with the given UUID
|
// Find the child requirement item with the given UUID
|
||||||
const auto it = ranges::find_if(m_children,
|
const auto childIt = ranges::find_if(m_children,
|
||||||
[&childUuid](const RequirementItemPtr &child)
|
[&childUuid](const RequirementItemPtr &child)
|
||||||
{ return child->metadata.uuid == childUuid; });
|
{ return child->metadata.uuid == childUuid; });
|
||||||
if (it != m_children.end())
|
if (childIt == m_children.end())
|
||||||
m_children.erase(it); // Remove the child requirement item from the vector of children
|
throw std::runtime_error("Child requirement item with UUID " + childUuid + " not found");
|
||||||
|
|
||||||
|
(*childIt)->removeChildren(); // Recursively remove all child requirement items of the child requirement item to be removed
|
||||||
|
m_children.erase(childIt); // Remove the child requirement item from the vector of children
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
/* Remove all child requirement items from this requirement item */
|
||||||
|
void RequirementItem::removeChildren()
|
||||||
|
{
|
||||||
|
while (!m_children.empty())
|
||||||
|
{
|
||||||
|
const auto child = m_children.back(); // Get the last child requirement item
|
||||||
|
removeChild(child->metadata.uuid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//--------------------------------------------------------------
|
//--------------------------------------------------------------
|
||||||
/* Get the vector of child requirement items of this requirement item */
|
/* Get the vector of child requirement items of this requirement item */
|
||||||
|
|||||||
@@ -24,10 +24,16 @@ class RequirementItem : public std::enable_shared_from_this<RequirementItem>
|
|||||||
explicit RequirementItem(RequirementManager &manager, // Constructor
|
explicit RequirementItem(RequirementManager &manager, // Constructor
|
||||||
const std::shared_ptr<RequirementItem> &parentRequirement = nullptr);
|
const std::shared_ptr<RequirementItem> &parentRequirement = nullptr);
|
||||||
|
|
||||||
|
// Management functions
|
||||||
|
[[nodiscard]] Requirement toRequirement() const; // Convert this requirement item to an api::requirement::Requirement struct and return it
|
||||||
|
void update(const Requirement &updatedData); // Update the data of this requirement item with the provided updated requirement data
|
||||||
|
|
||||||
// Children management functions
|
// Children management functions
|
||||||
void appendChild(const api::requirement::Requirement &child); // Append a child requirement item to this requirement item
|
RequirementItemPtr getParent() const; // Get the m_parent requirement item of this requirement item
|
||||||
void removeChild(const std::string &childUuid); // Remove a child requirement item from this requirement item by its UUID
|
void appendChild(const Requirement &child); // Append a child requirement item to this requirement item
|
||||||
[[nodiscard]] std::vector<RequirementItemPtr> getChildren() const; // Get the vector of child requirement items of this requirement item
|
void removeChild(const std::string &childUuid); // Remove a child requirement item from this requirement item by its UUID
|
||||||
|
void removeChildren(); // Remove all child requirement items from this requirement item
|
||||||
|
[[nodiscard]] std::vector<RequirementItemPtr> getChildren() const; // Get the vector of child requirement items of this requirement item
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
RequirementManager &m_requirementManager; // Reference to the requirement manager that owns this requirement item
|
RequirementManager &m_requirementManager; // Reference to the requirement manager that owns this requirement item
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "bars/menuBar.h"
|
#include "bars/menuBar.h"
|
||||||
#include "bars/statusBar.h"
|
#include "bars/statusBar.h"
|
||||||
#include "bars/toolBar.h"
|
#include "bars/toolBar.h"
|
||||||
|
#include "requirementsPanel/requirementsPanel.h"
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
@@ -18,7 +19,6 @@ using namespace gui;
|
|||||||
MainFrame::MainFrame(dBus::Bus &bus)
|
MainFrame::MainFrame(dBus::Bus &bus)
|
||||||
: wxFrame(nullptr, wxID_ANY, _("kwa.Fr"), wxDefaultPosition, wxDefaultSize)
|
: wxFrame(nullptr, wxID_ANY, _("kwa.Fr"), wxDefaultPosition, wxDefaultSize)
|
||||||
, dBus::wxNode(this, bus)
|
, dBus::wxNode(this, bus)
|
||||||
, m_bus(bus)
|
|
||||||
{
|
{
|
||||||
// Initialization
|
// Initialization
|
||||||
SetIcon(wxICON(AAAA_ICON));
|
SetIcon(wxICON(AAAA_ICON));
|
||||||
@@ -36,6 +36,8 @@ MainFrame::MainFrame(dBus::Bus &bus)
|
|||||||
CenterOnScreen();
|
CenterOnScreen();
|
||||||
|
|
||||||
subscribe(dBus::makeID("log.message"));
|
subscribe(dBus::makeID("log.message"));
|
||||||
|
|
||||||
|
// Iconize();
|
||||||
}
|
}
|
||||||
//--------------------------------------------------------------
|
//--------------------------------------------------------------
|
||||||
/* Creating controls */
|
/* Creating controls */
|
||||||
@@ -46,7 +48,10 @@ void MainFrame::createControls()
|
|||||||
const auto framePanel = new wxPanel(this /*, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSIMPLE_BORDER*/);
|
const auto framePanel = new wxPanel(this /*, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSIMPLE_BORDER*/);
|
||||||
m_toolBar = new ToolBar(framePanel);
|
m_toolBar = new ToolBar(framePanel);
|
||||||
|
|
||||||
m_pageNotebook = new wxNotebook(framePanel, wxID_ANY);
|
m_pageNotebook = new wxNotebook(framePanel, wxID_ANY);
|
||||||
|
m_requirementsPanel = new RequirementsPanel(m_pageNotebook, m_bus);
|
||||||
|
|
||||||
|
m_pageNotebook->AddPage(m_requirementsPanel, "Requirements", true);
|
||||||
|
|
||||||
// Controls positioning
|
// Controls positioning
|
||||||
const auto mainSizer = new wxBoxSizer(wxVERTICAL);
|
const auto mainSizer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ namespace gui
|
|||||||
class MenuBar;
|
class MenuBar;
|
||||||
class StatusBar;
|
class StatusBar;
|
||||||
class ToolBar;
|
class ToolBar;
|
||||||
|
class RequirementsPanel;
|
||||||
//--------------------------------------------------------------
|
//--------------------------------------------------------------
|
||||||
class MainFrame : public wxFrame
|
class MainFrame : public wxFrame
|
||||||
, public dBus::wxNode
|
, public dBus::wxNode
|
||||||
@@ -18,7 +19,7 @@ class MainFrame : public wxFrame
|
|||||||
public:
|
public:
|
||||||
MainFrame() = delete; // Default constructor
|
MainFrame() = delete; // Default constructor
|
||||||
virtual ~MainFrame() = default; // Default destructor
|
virtual ~MainFrame() = default; // Default destructor
|
||||||
MainFrame(MainFrame &obj) = delete; // Copy constructor
|
MainFrame(const MainFrame &obj) = delete; // Copy constructor
|
||||||
MainFrame(MainFrame &&obj) noexcept = delete; // Move constructor
|
MainFrame(MainFrame &&obj) noexcept = delete; // Move constructor
|
||||||
MainFrame &operator=(const MainFrame &obj) = delete; // Copy assignment operator
|
MainFrame &operator=(const MainFrame &obj) = delete; // Copy assignment operator
|
||||||
MainFrame &operator=(MainFrame &&obj) noexcept = delete; // Move assignment operator
|
MainFrame &operator=(MainFrame &&obj) noexcept = delete; // Move assignment operator
|
||||||
@@ -26,13 +27,14 @@ class MainFrame : public wxFrame
|
|||||||
explicit MainFrame(dBus::Bus &bus); // Constructor
|
explicit MainFrame(dBus::Bus &bus); // Constructor
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
dBus::Bus &m_bus; // Reference to the application bus
|
// Controls
|
||||||
|
|
||||||
MenuBar *m_menuBar = nullptr; // Menu bar handle
|
MenuBar *m_menuBar = nullptr; // Menu bar handle
|
||||||
StatusBar *m_statusBar = nullptr; // Status bar handle
|
StatusBar *m_statusBar = nullptr; // Status bar handle
|
||||||
ToolBar *m_toolBar = nullptr; // Toolbar handle
|
ToolBar *m_toolBar = nullptr; // Toolbar handle
|
||||||
wxNotebook *m_pageNotebook = nullptr; // Main notebook handle
|
wxNotebook *m_pageNotebook = nullptr; // Main notebook handle
|
||||||
|
|
||||||
|
RequirementsPanel *m_requirementsPanel = nullptr; // Requirements panel handle
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void createControls(); // Creating controls
|
void createControls(); // Creating controls
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,233 @@
|
|||||||
|
#include "requirementDetailPanel.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace gui;
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
/* Constructor */
|
||||||
|
RequirementDetailPanel::RequirementDetailPanel(wxWindow *parentWindow, dBus::Bus &bus)
|
||||||
|
: wxScrolledWindow(parentWindow, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSIMPLE_BORDER | wxVSCROLL)
|
||||||
|
, m_bus(bus)
|
||||||
|
{
|
||||||
|
// Initialization
|
||||||
|
SetScrollRate(0, 10); // Set the scroll rate for the panel (vertical only)
|
||||||
|
|
||||||
|
// Creating controls
|
||||||
|
createControls();
|
||||||
|
|
||||||
|
// Post initialization
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
/* Creating controls */
|
||||||
|
void RequirementDetailPanel::createControls()
|
||||||
|
{
|
||||||
|
const auto mainPanel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(500, -1), wxSIMPLE_BORDER);
|
||||||
|
|
||||||
|
// Controls positioning
|
||||||
|
const auto ctrlSizer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
ctrlSizer->Add(createControls_metadata(mainPanel), wxSizerFlags(0).Expand());
|
||||||
|
ctrlSizer->Add(createControls_details(mainPanel), wxSizerFlags(0).Expand());
|
||||||
|
ctrlSizer->Add(createControls_classification(mainPanel), wxSizerFlags(0).Expand());
|
||||||
|
mainPanel->SetSizer(ctrlSizer);
|
||||||
|
|
||||||
|
const auto mainSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
mainSizer->Add(mainPanel, wxSizerFlags(0).Expand());
|
||||||
|
mainSizer->AddStretchSpacer(1);
|
||||||
|
|
||||||
|
SetSizer(mainSizer);
|
||||||
|
|
||||||
|
// Set the virtual size to match the size of the child panel,
|
||||||
|
// enabling scrolling if necessary
|
||||||
|
FitInside();
|
||||||
|
|
||||||
|
// Harmonize the sizes of the title controls to ensure they have the same width
|
||||||
|
updateTitleSizes();
|
||||||
|
Layout();
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
/* Creating controls for requirement metadata (ID, UUID, name, description, etc.) */
|
||||||
|
wxSizer *RequirementDetailPanel::createControls_metadata(wxWindow *owner)
|
||||||
|
{
|
||||||
|
struct Item
|
||||||
|
{
|
||||||
|
wxWindow *titleCtrl;
|
||||||
|
wxWindow *inputCtrl;
|
||||||
|
};
|
||||||
|
std::vector<Item> items;
|
||||||
|
|
||||||
|
{
|
||||||
|
const Item item{ .titleCtrl = createTitle(owner, _T("UUID:")),
|
||||||
|
.inputCtrl = new wxTextCtrl(owner, wxID_ANY) };
|
||||||
|
items.push_back(item);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const Item item{ .titleCtrl = createTitle(owner, _T("ID:")),
|
||||||
|
.inputCtrl = new wxTextCtrl(owner, wxID_ANY) };
|
||||||
|
items.push_back(item);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const Item item{ .titleCtrl = createTitle(owner, _("Author:")),
|
||||||
|
.inputCtrl = new wxTextCtrl(owner, wxID_ANY) };
|
||||||
|
items.push_back(item);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const Item item{ .titleCtrl = createTitle(owner, _("Created at:")),
|
||||||
|
.inputCtrl = new wxTextCtrl(owner, wxID_ANY) };
|
||||||
|
items.push_back(item);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const Item item{ .titleCtrl = createTitle(owner, _("Updated at:")),
|
||||||
|
.inputCtrl = new wxTextCtrl(owner, wxID_ANY) };
|
||||||
|
items.push_back(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Controls positioning
|
||||||
|
const auto ctrlSizer = new wxFlexGridSizer(2, 5, 5);
|
||||||
|
for (const auto &item : items)
|
||||||
|
{
|
||||||
|
ctrlSizer->Add(item.titleCtrl, wxSizerFlags(0).Expand().CenterVertical());
|
||||||
|
ctrlSizer->Add(item.inputCtrl, wxSizerFlags(1).Expand());
|
||||||
|
}
|
||||||
|
ctrlSizer->AddGrowableCol(1, 1);
|
||||||
|
|
||||||
|
// Controls positioning
|
||||||
|
const auto localSizer = new wxStaticBoxSizer(wxVERTICAL, owner, "Metadata");
|
||||||
|
localSizer->Add(ctrlSizer, wxSizerFlags(0).Expand().Border(wxALL, 5));
|
||||||
|
|
||||||
|
return localSizer;
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
/* Creating controls for requirement details (e.g., custom attributes, child requirements, etc.) */
|
||||||
|
wxSizer *RequirementDetailPanel::createControls_details(wxWindow *owner)
|
||||||
|
{
|
||||||
|
struct Item
|
||||||
|
{
|
||||||
|
wxWindow *titleCtrl;
|
||||||
|
wxWindow *inputCtrl;
|
||||||
|
};
|
||||||
|
std::vector<Item> items;
|
||||||
|
|
||||||
|
{
|
||||||
|
const Item item{ .titleCtrl = createTitle(owner, _("Title:")),
|
||||||
|
.inputCtrl = new wxTextCtrl(owner, wxID_ANY) };
|
||||||
|
items.push_back(item);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const Item item{ .titleCtrl = createTitle(owner, _("Description:")),
|
||||||
|
.inputCtrl = new wxTextCtrl(owner,
|
||||||
|
wxID_ANY,
|
||||||
|
wxEmptyString,
|
||||||
|
wxDefaultPosition,
|
||||||
|
wxDefaultSize,
|
||||||
|
wxTE_PROCESS_TAB | wxTE_MULTILINE | wxVSCROLL | wxTE_AUTO_URL) };
|
||||||
|
items.push_back(item);
|
||||||
|
|
||||||
|
item.inputCtrl->SetMinSize(wxSize(-1, 200));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const Item item{ .titleCtrl = createTitle(owner, _("Acceptance criteria:")),
|
||||||
|
.inputCtrl = new wxTextCtrl(owner,
|
||||||
|
wxID_ANY,
|
||||||
|
wxEmptyString,
|
||||||
|
wxDefaultPosition,
|
||||||
|
wxDefaultSize,
|
||||||
|
wxTE_PROCESS_TAB | wxTE_MULTILINE | wxVSCROLL) };
|
||||||
|
items.push_back(item);
|
||||||
|
|
||||||
|
item.inputCtrl->SetMinSize(wxSize(-1, 100));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const Item item{ .titleCtrl = new wxPanel(owner),
|
||||||
|
.inputCtrl = new wxCheckBox(owner, wxID_ANY, _("S.M.A.R.T.")) };
|
||||||
|
items.push_back(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Controls positioning
|
||||||
|
const auto ctrlSizer = new wxFlexGridSizer(2, 5, 5);
|
||||||
|
for (const auto &item : items)
|
||||||
|
{
|
||||||
|
ctrlSizer->Add(item.titleCtrl, wxSizerFlags(0).Expand().CenterVertical());
|
||||||
|
ctrlSizer->Add(item.inputCtrl, wxSizerFlags(1).Expand());
|
||||||
|
}
|
||||||
|
ctrlSizer->AddGrowableCol(1, 1);
|
||||||
|
|
||||||
|
const auto localSizer = new wxStaticBoxSizer(wxVERTICAL, owner, "Details");
|
||||||
|
localSizer->Add(ctrlSizer, wxSizerFlags(0).Expand().Border(wxALL, 5));
|
||||||
|
|
||||||
|
return localSizer;
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
/* Creating controls for requirement classification (e.g., priority, severity, etc.) */
|
||||||
|
wxSizer *RequirementDetailPanel::createControls_classification(wxWindow *owner)
|
||||||
|
{
|
||||||
|
struct Item
|
||||||
|
{
|
||||||
|
wxWindow *titleCtrl;
|
||||||
|
wxWindow *inputCtrl;
|
||||||
|
};
|
||||||
|
std::vector<Item> items;
|
||||||
|
|
||||||
|
{
|
||||||
|
const Item item{ .titleCtrl = createTitle(owner, "Type:"),
|
||||||
|
.inputCtrl = new wxTextCtrl(owner, wxID_ANY) };
|
||||||
|
items.push_back(item);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const Item item{ .titleCtrl = createTitle(owner, "Category:"),
|
||||||
|
.inputCtrl = new wxTextCtrl(owner, wxID_ANY) };
|
||||||
|
items.push_back(item);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const Item item{ .titleCtrl = createTitle(owner, "Priority:"),
|
||||||
|
.inputCtrl = new wxTextCtrl(owner, wxID_ANY) };
|
||||||
|
items.push_back(item);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const Item item{ .titleCtrl = createTitle(owner, "Status:"),
|
||||||
|
.inputCtrl = new wxTextCtrl(owner, wxID_ANY) };
|
||||||
|
items.push_back(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Controls positioning
|
||||||
|
const auto ctrlSizer = new wxFlexGridSizer(2, 5, 5);
|
||||||
|
for (const auto &item : items)
|
||||||
|
{
|
||||||
|
ctrlSizer->Add(item.titleCtrl, wxSizerFlags(0).Expand().CenterVertical());
|
||||||
|
ctrlSizer->Add(item.inputCtrl, wxSizerFlags(1).Expand());
|
||||||
|
}
|
||||||
|
ctrlSizer->AddGrowableCol(1, 1);
|
||||||
|
|
||||||
|
const auto localSizer = new wxStaticBoxSizer(wxVERTICAL, owner, "Classification");
|
||||||
|
localSizer->Add(ctrlSizer, wxSizerFlags(0).Expand().Border(wxALL, 5));
|
||||||
|
|
||||||
|
return localSizer;
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
/* Helper function to create a section title control */
|
||||||
|
wxStaticText *RequirementDetailPanel::createTitle(wxWindow *owner, const wxString &title)
|
||||||
|
{
|
||||||
|
const auto ctrl = new wxStaticText(owner,
|
||||||
|
wxID_ANY,
|
||||||
|
title,
|
||||||
|
wxDefaultPosition,
|
||||||
|
wxDefaultSize,
|
||||||
|
wxST_NO_AUTORESIZE | wxALIGN_RIGHT);
|
||||||
|
|
||||||
|
m_titleControls.push_back(ctrl);
|
||||||
|
|
||||||
|
return ctrl;
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
/* Harmonize the sizes of the title controls to ensure they have the same width */
|
||||||
|
void RequirementDetailPanel::updateTitleSizes() const
|
||||||
|
{
|
||||||
|
int maxWidth = 0;
|
||||||
|
for (const auto &ctrl : m_titleControls)
|
||||||
|
{
|
||||||
|
maxWidth = std::max(maxWidth, ctrl->GetSize().GetWidth());
|
||||||
|
}
|
||||||
|
for (const auto &ctrl : m_titleControls)
|
||||||
|
{
|
||||||
|
ctrl->SetMinSize(wxSize(maxWidth, -1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <dBus/dBus.h>
|
||||||
|
#include <wx/wx.h>
|
||||||
|
|
||||||
|
namespace gui
|
||||||
|
{
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
class RequirementDetailPanel : public wxScrolledWindow
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RequirementDetailPanel() = delete; // Default constructor
|
||||||
|
virtual ~RequirementDetailPanel() = default; // Default destructor
|
||||||
|
RequirementDetailPanel(const RequirementDetailPanel &obj) = delete; // Copy constructor
|
||||||
|
RequirementDetailPanel(RequirementDetailPanel &&obj) noexcept = delete; // Move constructor
|
||||||
|
RequirementDetailPanel &operator=(const RequirementDetailPanel &obj) = delete; // Copy assignment operator
|
||||||
|
RequirementDetailPanel &operator=(RequirementDetailPanel &&obj) noexcept = delete; // Move assignment operator
|
||||||
|
|
||||||
|
explicit RequirementDetailPanel(wxWindow *parentWindow, dBus::Bus &bus); // Constructor
|
||||||
|
|
||||||
|
protected:
|
||||||
|
dBus::Bus &m_bus; // Reference to the application bus
|
||||||
|
std::vector<wxControl *> m_titleControls; // Titles controls for each section (metadata, details, classification)
|
||||||
|
|
||||||
|
private:
|
||||||
|
void createControls(); // Creating controls
|
||||||
|
wxSizer *createControls_metadata(wxWindow *owner); // Creating controls for requirement metadata
|
||||||
|
wxSizer *createControls_details(wxWindow *owner); // Creating controls for requirement details
|
||||||
|
wxSizer *createControls_classification(wxWindow *owner); // Creating controls for requirement classification
|
||||||
|
|
||||||
|
wxStaticText *createTitle(wxWindow *owner, const wxString &title); // Helper function to create a section title control
|
||||||
|
void updateTitleSizes() const; // Harmonize the sizes of the title controls to ensure they have the same width
|
||||||
|
};
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
} // namespace gui
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
#include "requirementListPanel.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace gui;
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
/* Constructor */
|
||||||
|
RequirementListPanel::RequirementListPanel(wxWindow *parentWindow, dBus::Bus &bus)
|
||||||
|
: wxPanel(parentWindow, wxID_ANY, wxDefaultPosition, wxSize(300, -1), wxSIMPLE_BORDER)
|
||||||
|
, m_bus(bus)
|
||||||
|
{
|
||||||
|
// Initialization
|
||||||
|
|
||||||
|
// Creating controls
|
||||||
|
createControls();
|
||||||
|
|
||||||
|
// Post initialization
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
/* Creating controls */
|
||||||
|
void RequirementListPanel::createControls()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <dBus/dBus.h>
|
||||||
|
#include <wx/wx.h>
|
||||||
|
|
||||||
|
namespace gui
|
||||||
|
{
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
class RequirementListPanel : public wxPanel
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RequirementListPanel() = delete; // Default constructor
|
||||||
|
virtual ~RequirementListPanel() = default; // Default destructor
|
||||||
|
RequirementListPanel(const RequirementListPanel &obj) = delete; // Copy constructor
|
||||||
|
RequirementListPanel(RequirementListPanel &&obj) noexcept = delete; // Move constructor
|
||||||
|
RequirementListPanel &operator=(const RequirementListPanel &obj) = delete; // Copy assignment operator
|
||||||
|
RequirementListPanel &operator=(RequirementListPanel &&obj) noexcept = delete; // Move assignment operator
|
||||||
|
|
||||||
|
explicit RequirementListPanel(wxWindow *parentWindow, dBus::Bus &bus); // Constructor
|
||||||
|
|
||||||
|
protected:
|
||||||
|
dBus::Bus &m_bus; // Reference to the application bus
|
||||||
|
|
||||||
|
private:
|
||||||
|
void createControls(); // Creating controls
|
||||||
|
};
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
} // namespace gui
|
||||||
40
src/gui/requirementsPanel/requirementsPanel.cpp
Normal file
40
src/gui/requirementsPanel/requirementsPanel.cpp
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#include "requirementsPanel.h"
|
||||||
|
|
||||||
|
#include "requirementDetailPanel/requirementDetailPanel.h"
|
||||||
|
#include "requirementListPanel/requirementListPanel.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace gui;
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
/* Constructor */
|
||||||
|
RequirementsPanel::RequirementsPanel(wxWindow *parentWindow, dBus::Bus &bus)
|
||||||
|
: wxPanel(parentWindow, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSIMPLE_BORDER)
|
||||||
|
, dBus::wxNode(this, bus)
|
||||||
|
{
|
||||||
|
// Initialization
|
||||||
|
|
||||||
|
// Creating controls
|
||||||
|
createControls();
|
||||||
|
|
||||||
|
// Post initialization
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
/* Creating controls */
|
||||||
|
void RequirementsPanel::createControls()
|
||||||
|
{
|
||||||
|
m_requirementListPanel = new RequirementListPanel(this, m_bus);
|
||||||
|
m_requirementDetailPanel = new RequirementDetailPanel(this, m_bus);
|
||||||
|
|
||||||
|
// Controls positioning
|
||||||
|
const auto mainSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
mainSizer->Add(m_requirementListPanel, wxSizerFlags(0).Expand().Border(wxALL, 5));
|
||||||
|
mainSizer->Add(m_requirementDetailPanel, wxSizerFlags(1).Expand().Border(wxALL, 5));
|
||||||
|
|
||||||
|
SetSizer(mainSizer);
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
/* dBus message reception handler */
|
||||||
|
void RequirementsPanel::on_receiveMessageFromBus(MessageID messageID, MessagePtr message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------
|
||||||
68
src/gui/requirementsPanel/requirementsPanel.h
Normal file
68
src/gui/requirementsPanel/requirementsPanel.h
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "gui/wxNode.h"
|
||||||
|
|
||||||
|
#include <dBus/dBus.h>
|
||||||
|
#include <wx/wx.h>
|
||||||
|
|
||||||
|
namespace gui
|
||||||
|
{
|
||||||
|
class RequirementDetailPanel;
|
||||||
|
class RequirementListPanel;
|
||||||
|
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
class RequirementsPanel : public wxPanel
|
||||||
|
, public dBus::wxNode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RequirementsPanel() = delete; // Default constructor
|
||||||
|
virtual ~RequirementsPanel() = default; // Default destructor
|
||||||
|
RequirementsPanel(const RequirementsPanel &obj) = delete; // Copy constructor
|
||||||
|
RequirementsPanel(RequirementsPanel &&obj) noexcept = delete; // Move constructor
|
||||||
|
RequirementsPanel &operator=(const RequirementsPanel &obj) = delete; // Copy assignment operator
|
||||||
|
RequirementsPanel &operator=(RequirementsPanel &&obj) noexcept = delete; // Move assignment operator
|
||||||
|
|
||||||
|
explicit RequirementsPanel(wxWindow *parentWindow, dBus::Bus &bus); // Constructor
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Controls
|
||||||
|
RequirementListPanel *m_requirementListPanel = nullptr; // Pointer to the requirement list panel control
|
||||||
|
RequirementDetailPanel *m_requirementDetailPanel = nullptr; // Pointer to the requirement detail panel control
|
||||||
|
|
||||||
|
private:
|
||||||
|
void createControls(); // Creating controls
|
||||||
|
|
||||||
|
// Events
|
||||||
|
void on_receiveMessageFromBus(MessageID messageID, MessagePtr message) override; // dBus message reception handler
|
||||||
|
};
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
} // namespace gui
|
||||||
|
|
||||||
|
/*
|
||||||
|
┌─────────────────────────────────────────────┐
|
||||||
|
│ ▼ Métadonnées │
|
||||||
|
│ ID: [REQ-001] 🔒 │
|
||||||
|
│ UUID: [abc-123-def] 🔒 │
|
||||||
|
│ Auteur: [John Doe] 🔒 │
|
||||||
|
│ Créé le: [2026-03-13 14:30] 🔒 │
|
||||||
|
│ Modifié le: [2026-03-13 15:45] 🔒 │
|
||||||
|
├─────────────────────────────────────────────┤
|
||||||
|
│ ▼ Détails │
|
||||||
|
│ Nom: [________________] │
|
||||||
|
│ Description: [ ] │
|
||||||
|
│ [ ] │
|
||||||
|
│ [________________] │
|
||||||
|
│ Critères: [ ] │
|
||||||
|
│ [________________] │
|
||||||
|
│ □ SMART │
|
||||||
|
├─────────────────────────────────────────────┤
|
||||||
|
│ ▼ Classification │
|
||||||
|
│ Type: [Functional ▼] │
|
||||||
|
│ Catégorie: [Backend ▼] │
|
||||||
|
│ Priorité: ● ● ● ○ ○ (3/5) │
|
||||||
|
│ Statut: ○ Draft ○ Review │
|
||||||
|
│ ● Approved ○ Rejected │
|
||||||
|
├─────────────────────────────────────────────┤
|
||||||
|
│ [Enregistrer] [Annuler] │
|
||||||
|
└─────────────────────────────────────────────┘
|
||||||
|
*/
|
||||||
@@ -67,9 +67,12 @@ class wxNode : private dBus::Node
|
|||||||
// derived class to handle incoming messages from the bus
|
// derived class to handle incoming messages from the bus
|
||||||
virtual void on_receiveMessageFromBus(MessageID messageID, MessagePtr message) = 0;
|
virtual void on_receiveMessageFromBus(MessageID messageID, MessagePtr message) = 0;
|
||||||
|
|
||||||
|
using Node::m_bus; // Reference to the bus this node is connected to
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
wxWindow *m_owner = nullptr; // Pointer to the owner window (for posting events to the GUI)
|
wxWindow *m_owner = nullptr; // Pointer to the owner window (for posting events to the GUI)
|
||||||
std::jthread m_eventThread; // Thread for listening to messages from the bus
|
|
||||||
|
std::jthread m_eventThread; // Thread for listening to messages from the bus
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void startEventThread(); // Start the event thread to listen for messages from the bus
|
void startEventThread(); // Start the event thread to listen for messages from the bus
|
||||||
|
|||||||
Reference in New Issue
Block a user