diff --git a/src/api/requirement.h b/src/api/requirement.h index a136eb4..68f92e4 100644 --- a/src/api/requirement.h +++ b/src/api/requirement.h @@ -28,7 +28,7 @@ struct Metadata //-------------------------------------------------------------- struct Details { - std::string title; // 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 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) @@ -65,7 +65,7 @@ struct GetRequirementEvent : dBus::api::DefaultData postGetRequirementRequest(dBus::Bus &bus, const std::string &uuid) +inline std::expected postGetRequirementRequest(dBus::Bus &bus, const std::string &uuid = "") { auto eventData = GetRequirementEvent(); eventData.uuid = std::move(uuid); diff --git a/src/core/projectManager/RequirementManager.cpp b/src/core/projectManager/RequirementManager.cpp index 25778d2..f3f6605 100644 --- a/src/core/projectManager/RequirementManager.cpp +++ b/src/core/projectManager/RequirementManager.cpp @@ -10,7 +10,8 @@ using namespace core::project; /* Default constructor */ RequirementManager::RequirementManager() { - m_rootRequirement = make_shared(*this); // Create the root requirement item and set it as the root requirement + m_rootRequirement = make_shared(*this); // Create the root requirement item and set it as the root requirement + m_requirementsByUuid[m_rootRequirement->metadata.uuid] = m_rootRequirement; // Register the root requirement item } //-------------------------------------------------------------- /* Get the root requirement */ @@ -28,9 +29,13 @@ std::vector RequirementManager::getRootR /* Get a requirement by its UUID */ RequirementManager::RequirementItemPtr RequirementManager::getRequirement(const std::string &uuid) const { + if (uuid.empty()) + return m_rootRequirement; // Return the root requirement item pointer if the UUID is empty + if (m_requirementsByUuid.contains(uuid)) return m_requirementsByUuid.at(uuid); // Return the requirement item pointer if found in the map - return {}; // Return nullptr if the requirement with the given UUID is not found + + return {}; // Return nullptr if the requirement with the given UUID is not found } //-------------------------------------------------------------- /* Get the vector of child requirement items of a requirement by its UUID */ diff --git a/src/core/projectManager/requirementItem.cpp b/src/core/projectManager/requirementItem.cpp index 971223b..4c6840b 100644 --- a/src/core/projectManager/requirementItem.cpp +++ b/src/core/projectManager/requirementItem.cpp @@ -14,7 +14,6 @@ RequirementItem::RequirementItem(RequirementManager &manager, const std::shared_ m_parent = parentRequirement; // Set the m_parent requirement item // Register this requirement item in the manager's internal map - m_requirementManager.registerRequirement(shared_from_this()); } //-------------------------------------------------------------- /* Convert this requirement item to an api::requirement::Requirement struct and return it */ @@ -52,6 +51,8 @@ void RequirementItem::appendChild(const Requirement &child) { // Create a new requirement item for the child requirement and set this requirement item as its m_parent const auto childItem = make_shared(m_requirementManager, shared_from_this()); + m_requirementManager.registerRequirement(childItem); // Register the child requirement item + childItem->update(child); // Update the child requirement item with the provided child requirement data // Append the child requirement item to the vector of children diff --git a/src/gui/mainFrame.cpp b/src/gui/mainFrame.cpp index 7a089ac..ac51f4d 100644 --- a/src/gui/mainFrame.cpp +++ b/src/gui/mainFrame.cpp @@ -45,13 +45,16 @@ MainFrame::MainFrame(dBus::Bus &bus) /* Creating controls */ void MainFrame::createControls() { - m_menuBar = new MenuBar(this); - m_statusBar = new StatusBar(this); + m_menuBar = new MenuBar(this); + m_statusBar = new StatusBar(this); + const auto framePanel = new wxPanel(this /*, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSIMPLE_BORDER*/); 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_requirementsPanel->SetBackgroundColour(framePanel->GetBackgroundColour()); m_pageNotebook->AddPage(m_requirementsPanel, "Requirements", true); diff --git a/src/gui/requirementsPanel/requirementDetailPanel/requirementDetailPanel.cpp b/src/gui/requirementsPanel/requirementDetailPanel/requirementDetailPanel.cpp index 01de239..1a77cf1 100644 --- a/src/gui/requirementsPanel/requirementDetailPanel/requirementDetailPanel.cpp +++ b/src/gui/requirementsPanel/requirementDetailPanel/requirementDetailPanel.cpp @@ -1,5 +1,7 @@ #include "requirementDetailPanel.h" +#include "resources/resources.h" + #include using namespace std; @@ -24,9 +26,9 @@ void RequirementDetailPanel::createControls() const auto mainPanel = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL); mainPanel->SetScrollRate(0, 10); // Set the scroll rate for the panel (vertical only) - const auto closeButton = new wxButton(this, wxID_ANY, _("Close")); - const auto saveButton = new wxButton(this, wxID_ANY, _("Save")); - const auto discardButton = new wxButton(this, wxID_ANY, _("Discard")); + const auto closeButton = new wxBitmapButton(this, wxID_ANY, icon_xmark_24x24(), wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW | wxBORDER_NONE); + const auto saveButton = new wxBitmapButton(this, wxID_ANY, icon_floppy_disk_24x24(), wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW | wxBORDER_NONE); + const auto discardButton = new wxBitmapButton(this, wxID_ANY, icon_rotate_left_24x24(), wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW | wxBORDER_NONE); // Controls positioning const auto ctrlSizer = new wxBoxSizer(wxVERTICAL); diff --git a/src/gui/requirementsPanel/requirementListPanel/requirementListPanel.cpp b/src/gui/requirementsPanel/requirementListPanel/requirementListPanel.cpp index 5e5c0bc..05ec642 100644 --- a/src/gui/requirementsPanel/requirementListPanel/requirementListPanel.cpp +++ b/src/gui/requirementsPanel/requirementListPanel/requirementListPanel.cpp @@ -1,5 +1,8 @@ #include "requirementListPanel.h" +#include "requirementTreeModel.h" +#include "resources/resources.h" + using namespace std; using namespace gui; //-------------------------------------------------------------- @@ -19,5 +22,47 @@ RequirementListPanel::RequirementListPanel(wxWindow *parentWindow, dBus::Bus &bu /* Creating controls */ void RequirementListPanel::createControls() { + // Create the data view control + m_dataViewCtrl = new wxDataViewCtrl(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSIMPLE_BORDER); + m_dataViewCtrl->AppendTextColumn(_T("ID"), 0, wxDATAVIEW_CELL_INERT, 100, wxALIGN_LEFT); + m_dataViewCtrl->AppendTextColumn(_("Title"), 1, wxDATAVIEW_CELL_INERT, 200, wxALIGN_LEFT); + + const auto model = new RequirementTreeModel(m_bus); + model->loadRootRequirement(); + m_dataViewCtrl->AssociateModel(model); + model->DecRef(); // The control now owns the model, so we can release our reference + + // Create other controls + const auto addButton = new wxBitmapButton(this, wxID_ANY, icon_plus_24x24(), wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW | wxBORDER_NONE); + addButton->Bind(wxEVT_BUTTON, &RequirementListPanel::on_addButtonClick, this); + + const auto reloadButton = new wxBitmapButton(this, wxID_ANY, icon_rotate_left_24x24(), wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW | wxBORDER_NONE); + reloadButton->Bind(wxEVT_BUTTON, &RequirementListPanel::on_reloadButtonClick, this); + + // Controls positioning + const auto buttonSizer = new wxBoxSizer(wxHORIZONTAL); + buttonSizer->Add(addButton, wxSizerFlags(0).Border(wxALL, 5)); + buttonSizer->AddStretchSpacer(); + buttonSizer->Add(reloadButton, wxSizerFlags(0).Border(wxALL, 5)); + + const auto mainSizer = new wxBoxSizer(wxVERTICAL); + mainSizer->Add(m_dataViewCtrl, wxSizerFlags(1).Expand().Border(wxALL, 5)); + mainSizer->Add(buttonSizer, wxSizerFlags(0).Expand()); + + SetSizer(mainSizer); +} +//-------------------------------------------------------------- +/* Process the add requirement click event */ +void RequirementListPanel::on_addButtonClick(wxCommandEvent &event) +{ +} +//-------------------------------------------------------------- +/* Process the reload requirements click event */ +void RequirementListPanel::on_reloadButtonClick(wxCommandEvent &event) +{ + // Reload all requirements + + if (auto *model = dynamic_cast(m_dataViewCtrl->GetModel())) + model->loadRootRequirement(); } //-------------------------------------------------------------- diff --git a/src/gui/requirementsPanel/requirementListPanel/requirementListPanel.h b/src/gui/requirementsPanel/requirementListPanel/requirementListPanel.h index 2ff9b34..379ee85 100644 --- a/src/gui/requirementsPanel/requirementListPanel/requirementListPanel.h +++ b/src/gui/requirementsPanel/requirementListPanel/requirementListPanel.h @@ -1,6 +1,8 @@ #pragma once #include +#include +#include #include namespace gui @@ -9,8 +11,8 @@ namespace gui class RequirementListPanel : public wxPanel { public: - RequirementListPanel() = delete; // Default constructor - virtual ~RequirementListPanel() = default; // Default destructor + 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 @@ -21,8 +23,15 @@ class RequirementListPanel : public wxPanel protected: dBus::Bus &m_bus; // Reference to the application bus + // Controls + wxDataViewCtrl *m_dataViewCtrl = nullptr; + private: void createControls(); // Creating controls + + // Events + void on_addButtonClick(wxCommandEvent &event); // Process the add requirement click event + void on_reloadButtonClick(wxCommandEvent &event); // Process the reload requirements click event }; //-------------------------------------------------------------- } // namespace gui diff --git a/src/gui/requirementsPanel/requirementListPanel/requirementTreeModel.cpp b/src/gui/requirementsPanel/requirementListPanel/requirementTreeModel.cpp new file mode 100644 index 0000000..267cb48 --- /dev/null +++ b/src/gui/requirementsPanel/requirementListPanel/requirementTreeModel.cpp @@ -0,0 +1,133 @@ +#include "requirementTreeModel.h" + +using namespace std; +using namespace gui; +//-------------------------------------------------------------- +/* Constructor */ +RequirementTreeModel::RequirementTreeModel(dBus::Bus &bus) + : m_bus(bus) +{ +} +//-------------------------------------------------------------- +void RequirementTreeModel::GetValue(wxVariant &variant, const wxDataViewItem &item, const unsigned col) const +{ + // Sanity check + if (!item.IsOk()) + return; + + const auto *data = static_cast(item.GetID()); + if (!data) + return; + + // Return the appropriate value based on the column index + switch (col) + { + case 0: // ID column + variant = data->id; + break; + case 1: // Title column + variant = data->title; + break; + default: + break; + } +} +//-------------------------------------------------------------- +bool RequirementTreeModel::SetValue(const wxVariant &variant, const wxDataViewItem &item, unsigned col) +{ + return {}; +} +//-------------------------------------------------------------- +wxDataViewItem RequirementTreeModel::GetParent(const wxDataViewItem &item) const +{ + if (!item.IsOk()) + return wxDataViewItem(nullptr); + + // Get the requirement data associated with the item + const auto *data = static_cast(item.GetID()); + if (!data) + return wxDataViewItem(nullptr); // If the item is invalid, return an invalid item + if (data == &m_rootRequirement) + return wxDataViewItem(nullptr); // If the item is the root requirement, return an invalid item + if (!data->parent) + return wxDataViewItem(nullptr); // If the item has no parent, return an invalid item + + return wxDataViewItem(data->parent); +} +//-------------------------------------------------------------- +bool RequirementTreeModel::IsContainer(const wxDataViewItem &item) const +{ + const auto *data = static_cast(item.GetID()); + if (!data) + return true; + + return !data->children.empty(); // An item is a container if it has children +} +//-------------------------------------------------------------- +bool RequirementTreeModel::HasContainerColumns(const wxDataViewItem &wxDataViewItem) const +{ + return true; +} +//-------------------------------------------------------------- +unsigned int RequirementTreeModel::GetChildren(const wxDataViewItem &item, wxDataViewItemArray &children) const +{ + const auto *data = static_cast(item.GetID()); + if (!data) // If the item is invalid, return the root requirement as the only child + { + children.Add(wxDataViewItem(const_cast(&m_rootRequirement))); + return 1; + } + + // Add the children of the current item to the children array + for (const auto &child : data->children) + { + children.Add(wxDataViewItem(child.get())); + } + return static_cast(data->children.size()); +} +//-------------------------------------------------------------- +void RequirementTreeModel::loadRootRequirement() +{ + // Get the root requirements from dBus + try + { + const auto ret = api::requirement::postGetRequirementRequest(m_bus); + if (!ret) + { + wxMessageBox("No manager found for requirement request.\n\n" + "Error loading root requirements.", + "Error", + wxOK | wxICON_ERROR); + return; + } + + m_rootRequirement = RequirementData(*ret); + } + catch (const std::exception &e) + { + // Handle the error (e.g., log it, show a message to the user, etc.) + // For now, we just print the error message to the console + std::cerr << "Error loading root requirements: " << e.what() << std::endl; + return; + } + + // Notify the control that the data has changed + //ItemChanged(wxDataViewItem(nullptr)); // Notify that the entire tree has changed + //ItemsAdded(wxDataViewItem(nullptr), wxDataViewItemArray{ wxDataViewItem(&m_rootRequirement) }); + Cleared(); +} +//-------------------------------------------------------------- +RequirementData *RequirementTreeModel::AddChildRequirement(RequirementData *parent, std::unique_ptr requirementData) +{ + return {}; +} +//-------------------------------------------------------------- +void RequirementTreeModel::clear() +{ +} +//-------------------------------------------------------------- +RequirementData *RequirementTreeModel::GetRequirementData(const wxDataViewItem &item) const +{ + return {}; +} +//-------------------------------------------------------------- diff --git a/src/gui/requirementsPanel/requirementListPanel/requirementTreeModel.h b/src/gui/requirementsPanel/requirementListPanel/requirementTreeModel.h index 4559234..13ad132 100644 --- a/src/gui/requirementsPanel/requirementListPanel/requirementTreeModel.h +++ b/src/gui/requirementsPanel/requirementListPanel/requirementTreeModel.h @@ -1,5 +1,7 @@ #pragma once +#include +#include #include #include #include @@ -9,18 +11,21 @@ namespace gui //-------------------------------------------------------------- struct RequirementData { - std::string uuid; std::string id; std::string title; - RequirementData *parent; + RequirementData *parent = nullptr; std::vector> children; - RequirementData(std::string uuid_, std::string id_, std::string title_) - : uuid(std::move(uuid_)) - , id(std::move(id_)) - , title(std::move(title_)) + RequirementData() { + id = "badID"; + title = "badTITLE"; + } + RequirementData(const api::requirement::Requirement &requirement) + { + id = "testID"; + title = "testTITLE"; } // Adds a child requirement @@ -39,34 +44,37 @@ struct RequirementData class RequirementTreeModel : public wxDataViewModel { public: - RequirementTreeModel(); // Default constructor + RequirementTreeModel() = delete; // Default constructor virtual ~RequirementTreeModel() = default; // Default destructor RequirementTreeModel(const RequirementTreeModel &obj) = delete; // Copy constructor RequirementTreeModel(RequirementTreeModel &&obj) noexcept = delete; // Move constructor RequirementTreeModel &operator=(const RequirementTreeModel &obj) = delete; // Copy assignment operator RequirementTreeModel &operator=(RequirementTreeModel &&obj) noexcept = delete; // Move assignment operator - // wxDataViewModel overrides methods - unsigned GetColumnCount() const override; - wxString GetColumnType(unsigned) const override; + explicit RequirementTreeModel(dBus::Bus &bus); // Constructor + // wxDataViewModel overrides methods void GetValue(wxVariant &variant, const wxDataViewItem &item, unsigned col) const override; bool SetValue(const wxVariant &variant, const wxDataViewItem &item, unsigned col) override; - wxDataViewItem GetParent(const wxDataViewItem &item) const override; - bool IsContainer(const wxDataViewItem &item) const override; - bool HasContainerColumns(const wxDataViewItem &) const override; + [[nodiscard]] wxDataViewItem GetParent(const wxDataViewItem &item) const override; + [[nodiscard]] bool IsContainer(const wxDataViewItem &item) const override; + [[nodiscard]] bool HasContainerColumns(const wxDataViewItem &) const override; - unsigned GetChildren(const wxDataViewItem &item, wxDataViewItemArray &children) const override; + unsigned int GetChildren(const wxDataViewItem &item, wxDataViewItemArray &children) const override; // Tree data model specific methods - RequirementData *AddRootRequirement(std::unique_ptr requirementData); // Add a root requirement to the tree + void loadRootRequirement(); // Add a root requirement to the tree + RequirementData *AddChildRequirement(RequirementData *parent, std::unique_ptr requirementData); // Add a child requirement to a parent requirement void clear(); RequirementData *GetRequirementData(const wxDataViewItem &item) const; // Get the requirement data associated with a given item protected: - std::vector> m_rootRequirement; + dBus::Bus &m_bus; // Reference to the application bus + + // std::vector> m_rootRequirement; + RequirementData m_rootRequirement; }; //-------------------------------------------------------------- } // namespace gui diff --git a/src/gui/requirementsPanel/requirementsPanel.cpp b/src/gui/requirementsPanel/requirementsPanel.cpp index 0177663..e3c771f 100644 --- a/src/gui/requirementsPanel/requirementsPanel.cpp +++ b/src/gui/requirementsPanel/requirementsPanel.cpp @@ -12,6 +12,8 @@ RequirementsPanel::RequirementsPanel(wxWindow *parentWindow, dBus::Bus &bus) , dBus::wxNode(this, bus) { // Initialization + SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + SetOwnBackgroundColour(GetBackgroundColour()); // Creating controls createControls();