Implementation of a dBus management class for the GUI
This commit is contained in:
@@ -4,7 +4,10 @@
|
||||
#include "bars/statusBar.h"
|
||||
#include "bars/toolBar.h"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
/* --- */
|
||||
#include "api/project.h"
|
||||
#include "api/requirement.h"
|
||||
/* --- */
|
||||
|
||||
@@ -14,6 +17,7 @@ using namespace gui;
|
||||
/* Constructor */
|
||||
MainFrame::MainFrame(dBus::Bus &bus)
|
||||
: wxFrame(nullptr, wxID_ANY, _("kwa.Fr"), wxDefaultPosition, wxDefaultSize)
|
||||
, dBus::wxNode(this, bus)
|
||||
, m_bus(bus)
|
||||
{
|
||||
// Initialization
|
||||
@@ -27,9 +31,11 @@ MainFrame::MainFrame(dBus::Bus &bus)
|
||||
createControls();
|
||||
|
||||
// Post-initialization
|
||||
m_menuBar->Bind(wxEVT_MENU, &MainFrame::on_menubarItemClick, this);
|
||||
m_menuBar->Bind(wxEVT_MENU, &MainFrame::on_menuBarItemClick, this);
|
||||
|
||||
CenterOnScreen();
|
||||
|
||||
subscribe(dBus::makeID("log.message"));
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Creating controls */
|
||||
@@ -56,7 +62,7 @@ void MainFrame::createProject()
|
||||
{
|
||||
try
|
||||
{
|
||||
const auto ret = postFileOperationRequest(m_bus, api::requirement::FileOperation::Create);
|
||||
const auto ret = api::project::postProjectOperationRequest(m_bus, api::project::OperationType::Create);
|
||||
if (!ret)
|
||||
{
|
||||
wxMessageBox("No manager found for project creation request.\n\n"
|
||||
@@ -80,30 +86,134 @@ void MainFrame::createProject()
|
||||
/* Open an existing project */
|
||||
void MainFrame::openProject()
|
||||
{
|
||||
try
|
||||
{
|
||||
wxFileDialog dlg(this,
|
||||
_("Open Project"),
|
||||
wxEmptyString,
|
||||
wxEmptyString,
|
||||
"Project Files (*.kwaProj)|*.kwaProj|All Files (*.*)|*.*",
|
||||
wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
if (dlg.ShowModal() != wxID_OK)
|
||||
return;
|
||||
|
||||
const auto filepath = filesystem::path(dlg.GetPath().ToStdString());
|
||||
const auto ret = api::project::postProjectOperationRequest(m_bus, api::project::OperationType::Open, filepath);
|
||||
if (!ret)
|
||||
{
|
||||
wxMessageBox("No manager found for project opening request.\n\n"
|
||||
"The manager responsible for handling project opening "
|
||||
"did not respond to the request. Please ensure that "
|
||||
"the project management component is running and try again.",
|
||||
"Failed...",
|
||||
wxOK | wxICON_WARNING,
|
||||
this);
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
wxMessageBox("An unexpected error occurred while opening a project\n\n" + wxString(e.what()),
|
||||
"Critical Error",
|
||||
wxOK | wxICON_ERROR,
|
||||
this);
|
||||
}
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Save the current project */
|
||||
void MainFrame::saveProject()
|
||||
{
|
||||
try
|
||||
{
|
||||
const auto ret = api::project::postProjectOperationRequest(m_bus, api::project::OperationType::Save);
|
||||
if (!ret)
|
||||
{
|
||||
wxMessageBox("No manager found for project saving request.\n\n"
|
||||
"The manager responsible for handling project saving "
|
||||
"did not respond to the request. Please ensure that "
|
||||
"the project management component is running and try again.",
|
||||
"Failed...",
|
||||
wxOK | wxICON_WARNING,
|
||||
this);
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
wxMessageBox("An unexpected error occurred while saving the project\n\n" + wxString(e.what()),
|
||||
"Critical Error",
|
||||
wxOK | wxICON_ERROR,
|
||||
this);
|
||||
}
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Save the current project as... */
|
||||
void MainFrame::saveProjectAs()
|
||||
{
|
||||
try
|
||||
{
|
||||
wxFileDialog dlg(this,
|
||||
_("Save Project"),
|
||||
wxEmptyString,
|
||||
wxEmptyString,
|
||||
"Project Files (*.kwaProj)|*.kwaProj|All Files (*.*)|*.*",
|
||||
wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||||
if (dlg.ShowModal() != wxID_OK)
|
||||
return;
|
||||
|
||||
const auto filepath = filesystem::path(dlg.GetPath().ToStdString());
|
||||
const auto ret = api::project::postProjectOperationRequest(m_bus, api::project::OperationType::SaveAs, filepath);
|
||||
if (!ret)
|
||||
{
|
||||
wxMessageBox("No manager found for project saving request.\n\n"
|
||||
"The manager responsible for handling project saving "
|
||||
"did not respond to the request. Please ensure that "
|
||||
"the project management component is running and try again.",
|
||||
"Failed...",
|
||||
wxOK | wxICON_WARNING,
|
||||
this);
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
wxMessageBox("An unexpected error occurred while saving the project\n\n" + wxString(e.what()),
|
||||
"Critical Error",
|
||||
wxOK | wxICON_ERROR,
|
||||
this);
|
||||
}
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Close the current project */
|
||||
void MainFrame::closeProject() const
|
||||
void MainFrame::closeProject()
|
||||
{
|
||||
try
|
||||
{
|
||||
const auto ret = api::project::postProjectOperationRequest(m_bus, api::project::OperationType::Close);
|
||||
if (!ret)
|
||||
{
|
||||
wxMessageBox("No manager found for project closing request.\n\n"
|
||||
"The manager responsible for handling project closing "
|
||||
"did not respond to the request. Please ensure that "
|
||||
"the project management component is running and try again.",
|
||||
"Failed...",
|
||||
wxOK | wxICON_WARNING,
|
||||
this);
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
wxMessageBox("An unexpected error occurred while closing the project\n\n" + wxString(e.what()),
|
||||
"Critical Error",
|
||||
wxOK | wxICON_ERROR,
|
||||
this);
|
||||
}
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Menu bar item click event */
|
||||
void MainFrame::on_menubarItemClick(wxCommandEvent &event)
|
||||
void MainFrame::on_menuBarItemClick(wxCommandEvent &event)
|
||||
{
|
||||
switch (static_cast<MenuBar::IDs>(event.GetId()))
|
||||
{
|
||||
using enum MenuBar::IDs;
|
||||
// File menu
|
||||
// File menu
|
||||
case ID_MENU_FILE_NEW:
|
||||
{
|
||||
createProject();
|
||||
@@ -135,17 +245,22 @@ void MainFrame::on_menubarItemClick(wxCommandEvent &event)
|
||||
break;
|
||||
}
|
||||
|
||||
// Requirement menu
|
||||
// Requirement menu
|
||||
case ID_MENU_REQ_CREATE:
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Help menu
|
||||
|
||||
// Help menu
|
||||
default:
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* dBus message reception handler */
|
||||
void MainFrame::on_receiveMessageFromBus(const MessageID messageID, const MessagePtr message)
|
||||
{
|
||||
cout << "From MainFrame::on_receiveMessageFromBus: " << message->toString() << endl;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "wxNode.h"
|
||||
|
||||
#include <dBus/dBus.h>
|
||||
#include <wx/notebook.h>
|
||||
#include <wx/wx.h>
|
||||
#include <dBus/dBus.h>
|
||||
|
||||
namespace gui
|
||||
{
|
||||
@@ -12,6 +13,7 @@ class StatusBar;
|
||||
class ToolBar;
|
||||
//--------------------------------------------------------------
|
||||
class MainFrame : public wxFrame
|
||||
, public dBus::wxNode
|
||||
{
|
||||
public:
|
||||
MainFrame() = delete; // Default constructor
|
||||
@@ -28,21 +30,22 @@ class MainFrame : public wxFrame
|
||||
|
||||
MenuBar *m_menuBar = nullptr; // Menu bar handle
|
||||
StatusBar *m_statusBar = nullptr; // Status bar handle
|
||||
ToolBar *m_toolBar = nullptr; // Tool bar handle
|
||||
ToolBar *m_toolBar = nullptr; // Toolbar handle
|
||||
wxNotebook *m_pageNotebook = nullptr; // Main notebook handle
|
||||
|
||||
private:
|
||||
void createControls(); // Creating controls
|
||||
|
||||
void createProject(); // Create a new project
|
||||
void openProject(); // Open an existing project
|
||||
void saveProject(); // Save the current project
|
||||
void saveProjectAs(); // Save the current project as...
|
||||
void closeProject() const; // Close the current project
|
||||
void createProject(); // Create a new project
|
||||
void openProject(); // Open an existing project
|
||||
void saveProject(); // Save the current project
|
||||
void saveProjectAs(); // Save the current project as...
|
||||
void closeProject(); // Close the current project
|
||||
|
||||
// Events
|
||||
public:
|
||||
void on_menubarItemClick(wxCommandEvent &event); // Menu bar item click event
|
||||
void on_menuBarItemClick(wxCommandEvent &event); // Menu bar item click event
|
||||
void on_receiveMessageFromBus(MessageID messageID, MessagePtr message) override; // dBus message reception handler
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
} // namespace gui
|
||||
|
||||
159
src/gui/wxNode.cpp
Normal file
159
src/gui/wxNode.cpp
Normal file
@@ -0,0 +1,159 @@
|
||||
#include "wxNode.h"
|
||||
|
||||
wxDEFINE_EVENT(EVT_DBUS_EVENT_MESSAGE, wxDBusCommandEvent);
|
||||
|
||||
using namespace std;
|
||||
using namespace dBus;
|
||||
//--------------------------------------------------------------
|
||||
/* Constructor */
|
||||
wxDBusCommandEvent::wxDBusCommandEvent(const dBus::HashID messageTypeID, MessagePtr message)
|
||||
: wxCommandEvent(EVT_DBUS_EVENT_MESSAGE, 0)
|
||||
, m_messageTypeID(messageTypeID)
|
||||
, m_message(std::move(message))
|
||||
{
|
||||
// Nothing to do
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Clone method for wxWidgets event system */
|
||||
wxEvent *wxDBusCommandEvent::Clone() const
|
||||
{
|
||||
auto *clonedEvent = new wxDBusCommandEvent(m_messageTypeID, m_message);
|
||||
clonedEvent->SetEventObject(GetEventObject()); // Copy the event object
|
||||
return clonedEvent;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Return the message type ID */
|
||||
HashID wxDBusCommandEvent::getMessageType() const
|
||||
{
|
||||
return m_messageTypeID;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Set the message type ID */
|
||||
void wxDBusCommandEvent::setDbusEventType(const dBus::HashID &messageTypeID)
|
||||
{
|
||||
m_messageTypeID = messageTypeID;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Set the message type ID based on event type string */
|
||||
void wxDBusCommandEvent::makeDbusEventType(const std::string &eventType)
|
||||
{
|
||||
m_messageTypeID = makeID(eventType);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Check if the event has an associated message */
|
||||
bool wxDBusCommandEvent::hasMessage() const
|
||||
{
|
||||
return m_message != nullptr;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get the associated message casted to a specific type */
|
||||
wxDBusCommandEvent::MessagePtr wxDBusCommandEvent::getMessage() const
|
||||
{
|
||||
return m_message;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Set the associated message for the event */
|
||||
void wxDBusCommandEvent::setMessage(const MessagePtr &message)
|
||||
{
|
||||
m_message = message;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
|
||||
/* --- */
|
||||
|
||||
//--------------------------------------------------------------
|
||||
/* Constructor */
|
||||
dBus::wxNode::wxNode(wxWindow *owner, dBus::Bus &bus)
|
||||
: Node(bus)
|
||||
, m_owner(owner)
|
||||
{
|
||||
// Sanity check
|
||||
if (!m_owner)
|
||||
throw std::invalid_argument("Owner window cannot be null");
|
||||
|
||||
// Bind the custom event handler to the owner window
|
||||
m_owner->Bind(EVT_DBUS_EVENT_MESSAGE, &wxNode::on_DBusEvent, this);
|
||||
|
||||
// Start the event thread to listen for messages from the bus
|
||||
startEventThread();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Destructor */
|
||||
dBus::wxNode::~wxNode()
|
||||
{
|
||||
// Stop the event thread to clean up resources
|
||||
stopEventThread();
|
||||
|
||||
// Unbind the custom event handler from the owner window
|
||||
if (m_owner)
|
||||
m_owner->Unbind(EVT_DBUS_EVENT_MESSAGE, &wxNode::on_DBusEvent, this);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Start the event thread to listen for messages from the bus */
|
||||
void dBus::wxNode::startEventThread()
|
||||
{
|
||||
// Start thread to process events related to the channel.
|
||||
// The thread will run until stop is requested.
|
||||
// clang-format off
|
||||
m_eventThread = std::jthread([this](const std::stop_token &stopToken)
|
||||
{
|
||||
// Process events until stop is requested
|
||||
while (!stopToken.stop_requested())
|
||||
{
|
||||
eventThreadLoop();
|
||||
}
|
||||
});
|
||||
// clang-format on
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Stop the event thread */
|
||||
void dBus::wxNode::stopEventThread()
|
||||
{
|
||||
// Stop event thread loop
|
||||
m_eventThread.request_stop();
|
||||
|
||||
// Wake up event thread if waiting
|
||||
notifyMessageQueue();
|
||||
|
||||
// Ensure thread has fully stopped before unbinding
|
||||
if (m_eventThread.joinable())
|
||||
m_eventThread.join();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* The main loop of the event thread that listens for messages
|
||||
* from the bus and posts events to the wxWidgets event system */
|
||||
void dBus::wxNode::eventThreadLoop()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Wait for a message to be received
|
||||
syncWaitForMessage();
|
||||
|
||||
// Process all messages in the queue
|
||||
while (getMessageCount() > 0)
|
||||
{
|
||||
const auto message = popNextMessage();
|
||||
if (!message)
|
||||
continue;
|
||||
|
||||
auto event = wxDBusCommandEvent(message->getMessageTypeID(), message);
|
||||
wxQueueEvent(m_owner, event.Clone());
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
wxLogError("Exception in wxDBus event thread: %s", e.what());
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Handler for wxDBusCommandEvent events posted to the GUI,
|
||||
* extracts the message and calls on_receiveMessageFromBus */
|
||||
void dBus::wxNode::on_DBusEvent(wxDBusCommandEvent &event)
|
||||
{
|
||||
const auto &messageID = event.getMessageType();
|
||||
const auto &message = event.getMessage();
|
||||
|
||||
on_receiveMessageFromBus(messageID, message);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
82
src/gui/wxNode.h
Normal file
82
src/gui/wxNode.h
Normal file
@@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
#include <dBus/dBus.h>
|
||||
#include <thread>
|
||||
#include <wx/wx.h>
|
||||
|
||||
//--------------------------------------------------------------
|
||||
class wxDBusCommandEvent : public wxCommandEvent
|
||||
{
|
||||
using MessagePtr = std::shared_ptr<dBus::Message>;
|
||||
|
||||
public:
|
||||
wxDBusCommandEvent() = delete; // Default constructor
|
||||
virtual ~wxDBusCommandEvent() = default; // Default destructor
|
||||
wxDBusCommandEvent(const wxDBusCommandEvent &obj) = delete; // Copy constructor
|
||||
wxDBusCommandEvent(wxDBusCommandEvent &&obj) noexcept = delete; // Move constructor
|
||||
wxDBusCommandEvent &operator=(const wxDBusCommandEvent &obj) = delete; // Copy assignment operator
|
||||
wxDBusCommandEvent &operator=(wxDBusCommandEvent &&obj) noexcept = delete; // Move assignment operator
|
||||
|
||||
explicit wxDBusCommandEvent(dBus::HashID messageTypeID, // Constructor
|
||||
MessagePtr message = {});
|
||||
|
||||
[[nodiscard]] wxEvent *Clone() const override; // Clone method for wxWidgets event system
|
||||
|
||||
// dBus message type management
|
||||
[[nodiscard]] dBus::HashID getMessageType() const; // Return the message type ID
|
||||
void setDbusEventType(const dBus::HashID &messageTypeID); // Set the message type ID
|
||||
void makeDbusEventType(const std::string &eventType); // Set the message type ID based on event type string
|
||||
|
||||
// dBus message payload management
|
||||
[[nodiscard]] bool hasMessage() const; // Check if the event has an associated message
|
||||
[[nodiscard]] MessagePtr getMessage() const; // Get the associated message casted to a specific type
|
||||
void setMessage(const MessagePtr &message); // Set the associated message for the event
|
||||
|
||||
protected:
|
||||
dBus::HashID m_messageTypeID; // Message type ID
|
||||
MessagePtr m_message; // Store the original dBus message
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
wxDECLARE_EVENT(EVT_DBUS_EVENT_MESSAGE, wxDBusCommandEvent);
|
||||
|
||||
/* --- */
|
||||
|
||||
namespace dBus
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
class wxNode : private dBus::Node
|
||||
{
|
||||
public:
|
||||
using MessageID = dBus::HashID;
|
||||
using MessagePtr = std::shared_ptr<dBus::Message>;
|
||||
|
||||
public:
|
||||
wxNode() = delete; // Default constructor
|
||||
virtual ~wxNode(); // Default destructor
|
||||
wxNode(const wxNode &obj) = delete; // Copy constructor
|
||||
wxNode(wxNode &&obj) noexcept = delete; // Move constructor
|
||||
wxNode &operator=(const wxNode &obj) = delete; // Copy assignment operator
|
||||
wxNode &operator=(wxNode &&obj) noexcept = delete; // Move assignment operator
|
||||
|
||||
explicit wxNode(wxWindow *owner, dBus::Bus &bus); // Constructor
|
||||
|
||||
using Node::subscribe; // Subscribe to a specific message type
|
||||
using Node::unsubscribe; // Unsubscribe from a specific message type
|
||||
|
||||
// Message reception handler to be overridden by the gui
|
||||
// derived class to handle incoming messages from the bus
|
||||
virtual void on_receiveMessageFromBus(MessageID messageID, MessagePtr message) = 0;
|
||||
|
||||
protected:
|
||||
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
|
||||
|
||||
private:
|
||||
void startEventThread(); // Start the event thread to listen for messages from the bus
|
||||
void stopEventThread(); // Stop the event thread
|
||||
void eventThreadLoop(); // Main loop event thread function
|
||||
|
||||
void on_DBusEvent(wxDBusCommandEvent &event); // Handler for wxDBusCommandEvent events
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
} // namespace dBus
|
||||
Reference in New Issue
Block a user