aboutsummaryrefslogtreecommitdiff
path: root/core/src/UI/UI_DatabaseView.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/UI/UI_DatabaseView.cpp')
-rw-r--r--core/src/UI/UI_DatabaseView.cpp274
1 files changed, 273 insertions, 1 deletions
diff --git a/core/src/UI/UI_DatabaseView.cpp b/core/src/UI/UI_DatabaseView.cpp
index 234aeaa..2b74918 100644
--- a/core/src/UI/UI_DatabaseView.cpp
+++ b/core/src/UI/UI_DatabaseView.cpp
@@ -1,9 +1,281 @@
#include "UI.hpp"
+#include "Model/Project.hpp"
#include "UI/Localization.hpp"
+#include "UI/States.hpp"
+#include "Utils/ScopeGuard.hpp"
+#include "cplt_fwd.hpp"
+#include <IconsFontAwesome.h>
+#include <SQLiteCpp/Statement.h>
#include <imgui.h>
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+namespace {
+
+// TODO move to Settings
+constexpr int kMaxEntriesPerPage = 20;
+
+class SaleEntry {
+public:
+ std::string Customer;
+ std::string Deadline;
+ std::string DeliveryTime;
+};
+
+class SalesViewTab {
+private:
+ Project* mProject;
+
+ /// Current active filter object, or \c nullptr.
+ std::unique_ptr<TableRowsFilter> mActiveFilter;
+
+ /// Inclusive.
+ /// \see mLastCachedRowId
+ int64_t mFirstCachedRowId;
+ /// Inclusive.
+ /// \see mFirstCachedRowId
+ int64_t mLastCachedRowId;
+
+ /// A cached, contiguous (row id of each entry is monotonically increasing, but not necessarily starts at 0) list ready-to-be-presented entries. May be incomplete.
+ std::vector<SaleEntry> mEntries;
+
+ // TODO this is very impractical (running filter in client code instead of letting SQLite doing it)
+ // Maybe simply cache a list of indices produced by a sql query?
+
+ /// A bitset of all active (should-be-displayed) entries based on current filter, updated whenever \c mActiveFilter is updated.
+ /// Should have the exact same size as \c entries.
+ std::vector<bool> mActiveEntries;
+
+ /// A cached list of index to entries that should be displayed on the current page.
+ std::vector<int> mCurrentPageEntries;
+
+ /// The current page the user is on.
+ int mCurrentPage;
+ /// Last possible page for the current set table and filter (inclusive).
+ int mLastPage;
+
+ /* UI states */
+
+ int mSelectedEntry;
+
+public:
+ void OnProjectChanged(Project* newProject) {
+ mProject = newProject;
+
+ mEntries.clear();
+ mCurrentPage = 0;
+ mLastPage = -1;
+
+ mSelectedEntry = -1;
+ }
+
+ void OnFilterChanged(std::unique_ptr<TableRowsFilter> filter) {
+ // TODO
+ }
+
+ void Draw() {
+ bool dummy = true;
+ auto ls = LocaleStrings::Instance.get();
+
+ if (ImGui::Button(ICON_FA_ARROW_LEFT, mCurrentPage == 0)) {
+ SetPage(mCurrentPage - 1);
+ }
+
+ ImGui::SameLine();
+ // +1 to convert from 0-based indices to 1-based, for human legibility
+ ImGui::Text("%d/%d", mCurrentPage + 1, mLastPage + 1);
+
+ ImGui::SameLine();
+ if (ImGui::Button(ICON_FA_ARROW_RIGHT, mCurrentPage == mLastPage)) {
+ SetPage(mCurrentPage + 1);
+ }
+
+ ImGui::SameLine();
+ if (ImGui::Button(ls->Edit.Get(), mSelectedEntry == -1)) {
+ ImGui::OpenPopup(ls->EditSaleEntryDialogTitle.Get());
+ }
+ if (ImGui::BeginPopupModal(ls->EditSaleEntryDialogTitle.Get(), &dummy, ImGuiWindowFlags_AlwaysAutoResize)) {
+ // TODO
+ ImGui::EndPopup();
+ }
+
+ if (ImGui::BeginTable("##SalesTable", 3)) {
+
+ ImGui::TableSetupColumn(ls->DatabaseCustomerColumn.Get());
+ ImGui::TableSetupColumn(ls->DatabaseDeadlineColumn.Get());
+ ImGui::TableSetupColumn(ls->DatabaseDeliveryTimeColumn.Get());
+ ImGui::TableHeadersRow();
+
+ for (int i : mCurrentPageEntries) {
+ auto& entry = mEntries[i];
+
+ ImGui::TableNextColumn();
+ if (ImGui::Selectable(entry.Customer.c_str(), mSelectedEntry == i)) {
+ mSelectedEntry = i;
+ }
+
+ ImGui::NextColumn();
+ ImGui::Text("%s", entry.Deadline.c_str());
+
+ ImGui::NextColumn();
+ if (entry.DeliveryTime.empty()) {
+ ImGui::Text("%s", ls->NotDeliveredMessage.Get());
+ } else {
+ ImGui::Text("%s", entry.DeliveryTime.c_str());
+ }
+ }
+
+ ImGui::EndTable();
+ }
+ }
+
+private:
+ void SetPage(int page) {
+ if (mCurrentPage != page) return;
+
+ mCurrentPage = page;
+ EnsureCacheCoversPage(page);
+
+ auto [begin, end] = CalcRangeForPage(page);
+ begin -= mFirstCachedRowId;
+ end -= mFirstCachedRowId;
+
+ mCurrentPageEntries.clear();
+ for (auto i = begin; i < end; ++i) {
+ if (mActiveEntries[i]) {
+ mCurrentPageEntries.push_back(i);
+ }
+ }
+ }
+
+ void EnsureCacheCoversPage(int page) {
+ auto [begin, end] = CalcRangeForPage(page);
+ EnsureCacheCovers(begin, end - 1);
+ }
+
+ void EnsureCacheCovers(int64_t firstRow, int64_t lastRow) {
+ if (firstRow > lastRow) {
+ std::swap(firstRow, lastRow);
+ }
+
+ int newFirst = mFirstCachedRowId;
+ int newLast = mLastCachedRowId;
+
+ bool doRebuild = false;
+ if (firstRow < mFirstCachedRowId) {
+ newFirst = CalcPageForRowId(firstRow) * kMaxEntriesPerPage;
+ doRebuild = true;
+ }
+ if (lastRow > mLastCachedRowId) {
+ newLast = CalcPageForRowId(lastRow) * kMaxEntriesPerPage;
+ doRebuild = true;
+ }
+ if (!doRebuild) return;
+
+ auto front = LoadRange(newFirst, mFirstCachedRowId);
+ auto back = LoadRange(mLastCachedRowId + 1, newLast + 1);
+
+ mEntries.insert(mEntries.begin(), front.begin(), front.end());
+ mEntries.insert(mEntries.end(), back.begin(), back.end());
+ // TODO update mActiveEntries
+
+ mFirstCachedRowId = newFirst;
+ mLastCachedRowId = newLast;
+ }
+
+ std::vector<SaleEntry> LoadRange(int64_t begin, int64_t end) {
+ std::vector<SaleEntry> result;
+
+ size_t size = end - begin;
+ if (size == 0) {
+ return result;
+ }
+
+ result.reserve(size);
+
+ auto& stmt = mProject->GetDatabase().GetSales().GetRowsStatement;
+ DEFER {
+ stmt.reset();
+ };
+
+ stmt.bind(1, begin);
+ stmt.bind(2, end);
+
+ while (true) {
+ bool hasResult = stmt.executeStep();
+ if (hasResult) {
+ result.push_back(stmt.getColumns<SaleEntry, 3>());
+ } else {
+ return result;
+ }
+ }
+ }
+
+ static int CalcPageForRowId(int64_t rowId) {
+ return rowId / kMaxEntriesPerPage;
+ }
+
+ /// Calculate range [begin, end) of row ids that the path-th page would show.
+ static std::pair<int64_t, int64_t> CalcRangeForPage(int page) {
+ int begin = page * kMaxEntriesPerPage;
+ return { begin, begin + kMaxEntriesPerPage };
+ }
+};
+
+class PurchaseEntry {
+public:
+ std::string Factory;
+ std::string OrderTime;
+ std::string DeliveryTime;
+};
+
+class PurchasesViewTab {
+private:
+ std::vector<PurchaseEntry> mEntries;
+ int mCurrentPage = 0;
+
+public:
+ void OnProjectChanged(Project* newProject) {
+ // TODO
+ }
+
+ void Draw() {
+ auto ls = LocaleStrings::Instance.get();
+ if (ImGui::BeginTable("##PurcahsesTable", 4)) {
+ ImGui::EndTable();
+ }
+ }
+};
+} // namespace
void UI::DatabaseViewTab() {
- // TODO
+ auto ls = LocaleStrings::Instance.get();
+ auto& uis = UIState::GetInstance();
+
+ static Project* currentProject = nullptr;
+ static auto salesView = std::make_unique<SalesViewTab>();
+ static auto purchasesView = std::make_unique<PurchasesViewTab>();
+
+ if (currentProject != uis.CurrentProject.get()) {
+ currentProject = uis.CurrentProject.get();
+ salesView->OnProjectChanged(currentProject);
+ purchasesView->OnProjectChanged(currentProject);
+ }
+
+ if (ImGui::BeginTabBar("##DatabaseViewTabs")) {
+ if (ImGui::BeginTabItem(ls->SalesViewTab.Get())) {
+ salesView->Draw();
+ ImGui::EndTabItem();
+ }
+ if (ImGui::BeginTabItem(ls->PurchasesViewTab.Get())) {
+ purchasesView->Draw();
+ ImGui::EndTabItem();
+ }
+ // if (ImGui::BeginTabItem(ls->DeliveriesTableTab.Get())) {
+ // ImGui::EndTabItem();
+ // }
+ }
}