From 9d054d411947a24a1b96ceaa5013e8ec1c8f6a3a Mon Sep 17 00:00:00 2001 From: Selim Mustafaev Date: Mon, 21 Nov 2022 23:25:52 +0300 Subject: [PATCH] Massive refactoring --- App.cpp | 10 ++++ App.h | 17 +++++++ CMakeLists.txt | 24 ++++++--- gtkpp/Application.cpp | 2 +- gtkpp/Box.cpp | 24 ++++++--- gtkpp/Box.h | 3 +- gtkpp/Button.cpp | 4 ++ gtkpp/Button.h | 1 + gtkpp/Dialog.cpp | 55 ++++++++++++++++++++ gtkpp/Dialog.h | 20 +++++++- gtkpp/Entry.cpp | 6 ++- gtkpp/Entry.h | 2 + gtkpp/HeaderBar.cpp | 8 +++ gtkpp/HeaderBar.h | 2 + gtkpp/Leaflet.cpp | 4 +- gtkpp/Leaflet.h | 2 +- gtkpp/ListItemFactory.cpp | 27 ++++++++++ gtkpp/ListItemFactory.h | 26 ++++++++++ gtkpp/ListView.cpp | 13 +++++ gtkpp/ListView.h | 21 ++++++++ gtkpp/ScrolledWindow.cpp | 15 ++++++ gtkpp/ScrolledWindow.h | 19 +++++++ gtkpp/SelectionModel.cpp | 26 ++++++++++ gtkpp/SelectionModel.h | 29 +++++++++++ gtkpp/Separator.cpp | 9 ++++ gtkpp/Separator.h | 19 +++++++ gtkpp/Widget.cpp | 17 +++++++ gtkpp/Widget.h | 4 ++ gtkpp/Window.cpp | 44 ++++++++++++---- gtkpp/Window.h | 15 ++++-- gui/AddNumberDialog.cpp | 42 ++++++++++------ gui/AddNumberDialog.h | 19 +++++-- gui/AddNumberDialog.xml | 32 ------------ gui/LoginWindow.cpp | 19 +++---- gui/LoginWindow.h | 3 +- gui/MainWindow.cpp | 52 ++++++++++++++++--- gui/MainWindow.h | 3 +- main.cpp | 17 ++++--- models/Engine.cpp | 5 ++ models/Engine.h | 21 ++++++++ models/JsonOptional.h | 75 ++++++++++++++++++++++++++++ models/Vehicle.cpp | 91 ++++++++++++++++++++++++++++++++++ models/Vehicle.h | 42 ++++++++++++++++ models/VehiclesListFactory.cpp | 9 ++++ models/VehiclesListFactory.h | 16 ++++++ services/Api.cpp | 17 ++++++- services/Api.h | 2 + services/IDBEntity.cpp | 41 +++++++++++++++ services/IDBEntity.h | 31 ++++++++++++ services/Settings.cpp | 10 ++-- services/Settings.h | 3 ++ services/Storage.cpp | 42 ++++++++++++++++ services/Storage.h | 64 ++++++++++++++++++++++++ 53 files changed, 1004 insertions(+), 120 deletions(-) create mode 100644 App.cpp create mode 100644 App.h create mode 100644 gtkpp/ListItemFactory.cpp create mode 100644 gtkpp/ListItemFactory.h create mode 100644 gtkpp/ListView.cpp create mode 100644 gtkpp/ListView.h create mode 100644 gtkpp/ScrolledWindow.cpp create mode 100644 gtkpp/ScrolledWindow.h create mode 100644 gtkpp/SelectionModel.cpp create mode 100644 gtkpp/SelectionModel.h create mode 100644 gtkpp/Separator.cpp create mode 100644 gtkpp/Separator.h delete mode 100644 gui/AddNumberDialog.xml create mode 100644 models/Engine.cpp create mode 100644 models/Engine.h create mode 100644 models/JsonOptional.h create mode 100644 models/Vehicle.cpp create mode 100644 models/Vehicle.h create mode 100644 models/VehiclesListFactory.cpp create mode 100644 models/VehiclesListFactory.h create mode 100644 services/IDBEntity.cpp create mode 100644 services/IDBEntity.h create mode 100644 services/Storage.cpp create mode 100644 services/Storage.h diff --git a/App.cpp b/App.cpp new file mode 100644 index 0000000..3f72e62 --- /dev/null +++ b/App.cpp @@ -0,0 +1,10 @@ +// +// Created by selim on 01.10.22. +// + +#include "App.h" + +App &App::instance() { + static App app("pro.aliencat.autocat"); + return app; +} diff --git a/App.h b/App.h new file mode 100644 index 0000000..502cfab --- /dev/null +++ b/App.h @@ -0,0 +1,17 @@ +// +// Created by selim on 01.10.22. +// + +#ifndef AUTOCAT_GNOME_APP_H +#define AUTOCAT_GNOME_APP_H + +#include "gtkpp/Application.h" + +class App: public gtkpp::Application { +public: + using gtkpp::Application::Application; + static App& instance(); +}; + + +#endif //AUTOCAT_GNOME_APP_H diff --git a/CMakeLists.txt b/CMakeLists.txt index 23bfa78..cfe3f54 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,12 +17,12 @@ if(APPLE) set(OPENSSL_LIBRARIES /usr/local/opt/openssl/lib) endif() -#list(APPEND CMAKE_MODULE_PATH /usr/local/lib/cmake/concurrencpp-0.1.4) - find_package(PkgConfig REQUIRED) find_package(nlohmann_json REQUIRED) find_package(concurrencpp REQUIRED) find_package(Threads REQUIRED) +find_package(SQLite3 REQUIRED) +find_package(fmt REQUIRED) pkg_check_modules(GTK REQUIRED gtk4) pkg_check_modules(GLIB REQUIRED glib-2.0) @@ -30,8 +30,6 @@ pkg_check_modules(LIBSOUP REQUIRED libsoup-2.4) pkg_check_modules(LIBADWAITA REQUIRED libadwaita-1) pkg_check_modules(LIBSIGCPP REQUIRED sigc++-3.0) -#ExternalProject_Add(concurrencpp GIT_REPOSITORY https://github.com/David-Haim/concurrencpp) - include_directories(${GTK_INCLUDE_DIRS} ${GLIB_INCLUDE_DIRS} ${LIBSOUP_INCLUDE_DIRS} @@ -77,7 +75,17 @@ add_executable(autocat_gnome main.cpp gtkpp/MessageDialog.cpp gtkpp/MessageDialog.h gtkpp/Leaflet.cpp - gtkpp/Leaflet.h gtkpp/Dialog.cpp gtkpp/Dialog.h gui/AddNumberDialog.cpp gui/AddNumberDialog.h gui/custom/PlateView.h gui/custom/PlateView.c) + gtkpp/Leaflet.h + gtkpp/Dialog.cpp + gtkpp/Dialog.h + gui/AddNumberDialog.cpp + gui/AddNumberDialog.h + gui/custom/PlateView.h + gui/custom/PlateView.c + App.cpp + App.h + models/Vehicle.cpp + models/Vehicle.h services/Storage.cpp services/Storage.h models/Engine.cpp models/Engine.h services/IDBEntity.h models/JsonOptional.h services/IDBEntity.cpp gtkpp/ScrolledWindow.cpp gtkpp/ScrolledWindow.h gtkpp/ListView.cpp gtkpp/ListView.h gtkpp/SelectionModel.cpp gtkpp/SelectionModel.h gtkpp/ListItemFactory.cpp gtkpp/ListItemFactory.h models/VehiclesListFactory.cpp models/VehiclesListFactory.h gtkpp/Separator.cpp gtkpp/Separator.h) target_link_libraries(autocat_gnome ${GTK_LIBRARIES} ${GLIB_LIBRARIES} @@ -86,9 +94,11 @@ target_link_libraries(autocat_gnome ${GTK_LIBRARIES} ${LIBSIGCPP_LIBRARIES} nlohmann_json::nlohmann_json concurrencpp::concurrencpp - Threads::Threads) + Threads::Threads + SQLite::SQLite3 + fmt::fmt) -set(XML gui/MainWindow.xml gui/AddNumberDialog.xml) +set(XML gui/MainWindow.xml) list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/Modules") include(GlibCompileResourcesSupport) diff --git a/gtkpp/Application.cpp b/gtkpp/Application.cpp index a827e61..7d4a861 100644 --- a/gtkpp/Application.cpp +++ b/gtkpp/Application.cpp @@ -13,7 +13,7 @@ namespace gtkpp { } Application::Application(const std::string &id) { - _app = adw_application_new(id.c_str(), G_APPLICATION_FLAGS_NONE); + _app = adw_application_new(id.c_str(), G_APPLICATION_DEFAULT_FLAGS); g_signal_connect(_app, "activate", G_CALLBACK(activateCallback), this); } diff --git a/gtkpp/Box.cpp b/gtkpp/Box.cpp index d25b081..ac85c1c 100644 --- a/gtkpp/Box.cpp +++ b/gtkpp/Box.cpp @@ -4,14 +4,22 @@ #include "Box.h" -gtkpp::Box::Box(GtkOrientation orientation, int spacing) : Widget() { - _widget = gtk_box_new(orientation, spacing); -} +namespace gtkpp { -void gtkpp::Box::append(const gtkpp::Widget& widget) { - gtk_box_append(GTK_BOX(_widget), widget.gobj()); -} + Box::Box(GtkWidget *widget): Widget() { + _widget = widget; + } + + Box::Box(GtkOrientation orientation, int spacing) : Widget() { + _widget = gtk_box_new(orientation, spacing); + } + + void Box::append(const gtkpp::Widget& widget) { + gtk_box_append(GTK_BOX(_widget), widget.gobj()); + } + + void Box::append(GtkWidget *widget) { + gtk_box_append(GTK_BOX(_widget), widget); + } -void gtkpp::Box::append(GtkWidget *widget) { - gtk_box_append(GTK_BOX(_widget), widget); } diff --git a/gtkpp/Box.h b/gtkpp/Box.h index 95eeb48..ca8dfde 100644 --- a/gtkpp/Box.h +++ b/gtkpp/Box.h @@ -11,8 +11,9 @@ namespace gtkpp { - class Box: public Widget{ + class Box: public Widget { public: + Box(GtkWidget* widget); Box(GtkOrientation orientation, int spacing); void append(const Widget& widget); void append(GtkWidget* widget); diff --git a/gtkpp/Button.cpp b/gtkpp/Button.cpp index edfd2fa..46f6dd4 100644 --- a/gtkpp/Button.cpp +++ b/gtkpp/Button.cpp @@ -15,4 +15,8 @@ namespace gtkpp { gtk_button_set_label(GTK_BUTTON(_widget), title.c_str()); } + void Button::setIconName(const std::string &name) { + gtk_button_set_icon_name(GTK_BUTTON(_widget), name.c_str()); + } + } diff --git a/gtkpp/Button.h b/gtkpp/Button.h index 84a34ba..f65ef63 100644 --- a/gtkpp/Button.h +++ b/gtkpp/Button.h @@ -15,6 +15,7 @@ namespace gtkpp { using Widget::Widget; Button(); void setTitle(const std::string& title); + void setIconName(const std::string& name); }; } diff --git a/gtkpp/Dialog.cpp b/gtkpp/Dialog.cpp index 3438d6e..bbc2355 100644 --- a/gtkpp/Dialog.cpp +++ b/gtkpp/Dialog.cpp @@ -3,3 +3,58 @@ // #include "Dialog.h" + +namespace gtkpp { + + void responseCallback(GtkDialog*, GtkResponseType responseId, void* data) { + auto dialog = reinterpret_cast(data); + dialog->_response = responseId; + dialog->_signalResponse.emit(responseId); + } + + Dialog::Dialog(Window* parent) { + + auto flags = static_cast(GTK_DIALOG_DESTROY_WITH_PARENT + | GTK_DIALOG_MODAL + | GTK_DIALOG_USE_HEADER_BAR); + + auto dialog = gtk_dialog_new_with_buttons("Add plate number", + parent->gobj(), + flags, + "OK", GTK_RESPONSE_OK, + "Cancel", GTK_RESPONSE_CANCEL, + nullptr); + _window = GTK_WINDOW(dialog); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); + _responseHandlerId = g_signal_connect (dialog, "response", G_CALLBACK(responseCallback), this); + } + + void Dialog::onResponse(const std::function &callback) { + _signalResponse.connect(callback); + } + + cc::result Dialog::responseAsync() { + auto promise = std::make_shared>(); + auto result = promise->get_result(); + + _signalResponse.connect([promise](GtkResponseType response){ + promise->set_result(response); + }); + + return result; + } + + Dialog::~Dialog() { + g_signal_handler_disconnect(_window, _responseHandlerId); + } + + GtkResponseType Dialog::currentResponse() const { + return _response; + } + + Box Dialog::contentWidget() const { + auto contentArea = gtk_dialog_get_content_area (GTK_DIALOG(_window)); + return {contentArea}; + } + +} diff --git a/gtkpp/Dialog.h b/gtkpp/Dialog.h index 13a41fd..1299e1d 100644 --- a/gtkpp/Dialog.h +++ b/gtkpp/Dialog.h @@ -6,12 +6,30 @@ #define AUTOCAT_GNOME_DIALOG_H #include "Window.h" +#include "Box.h" +#include +#include + +namespace cc = concurrencpp; namespace gtkpp { class Dialog: public Window { + private: + GtkResponseType _response = GTK_RESPONSE_NONE; + gulong _responseHandlerId; + sigc::signal _signalResponse; + friend void responseCallback(GtkDialog*, GtkResponseType responseId, void* data); + public: - using Window::Window; + void onResponse(const std::function& callback); + + public: + explicit Dialog(Window* parent); + ~Dialog(); + cc::result responseAsync(); + GtkResponseType currentResponse() const; + Box contentWidget() const; }; } diff --git a/gtkpp/Entry.cpp b/gtkpp/Entry.cpp index a65143a..81e2c80 100644 --- a/gtkpp/Entry.cpp +++ b/gtkpp/Entry.cpp @@ -13,7 +13,11 @@ namespace gtkpp { Entry::Entry() : Widget() { _widget = gtk_entry_new(); - g_signal_connect(_widget, "changed", G_CALLBACK(changedCallback), this); + _changedHandlerId = g_signal_connect(_widget, "changed", G_CALLBACK(changedCallback), this); + } + + Entry::~Entry() { + g_signal_handler_disconnect(_widget, _changedHandlerId); } void Entry::setPlaceholder(const std::string &placeholder) { diff --git a/gtkpp/Entry.h b/gtkpp/Entry.h index e55640f..0a3526f 100644 --- a/gtkpp/Entry.h +++ b/gtkpp/Entry.h @@ -13,6 +13,7 @@ namespace gtkpp { class Entry: public Widget { private: + gulong _changedHandlerId; sigc::signal _signalChanged; private: @@ -20,6 +21,7 @@ namespace gtkpp { public: Entry(); + ~Entry(); void setPlaceholder(const std::string& placeholder); void setPurpose(GtkInputPurpose purpose); void setVisibility(bool visibility); diff --git a/gtkpp/HeaderBar.cpp b/gtkpp/HeaderBar.cpp index c2cafc4..4a62189 100644 --- a/gtkpp/HeaderBar.cpp +++ b/gtkpp/HeaderBar.cpp @@ -18,4 +18,12 @@ namespace gtkpp { void HeaderBar::setTitle(const std::string& title) { adw_header_bar_set_title_widget(ADW_HEADER_BAR(_widget), gtk_label_new(title.c_str())); } + + void HeaderBar::showEndButtons(bool show) { + adw_header_bar_set_show_end_title_buttons(ADW_HEADER_BAR(_widget), show); + } + + void HeaderBar::packStart(const Widget &widget) { + adw_header_bar_pack_start(ADW_HEADER_BAR(_widget), widget.gobj()); + } } \ No newline at end of file diff --git a/gtkpp/HeaderBar.h b/gtkpp/HeaderBar.h index 5ff94a5..f961f78 100644 --- a/gtkpp/HeaderBar.h +++ b/gtkpp/HeaderBar.h @@ -15,6 +15,8 @@ namespace gtkpp { HeaderBar(); explicit HeaderBar(const std::string& title); void setTitle(const std::string& title); + void showEndButtons(bool show); + void packStart(const Widget& widget); }; } diff --git a/gtkpp/Leaflet.cpp b/gtkpp/Leaflet.cpp index 9a871bc..faccfc5 100644 --- a/gtkpp/Leaflet.cpp +++ b/gtkpp/Leaflet.cpp @@ -11,8 +11,8 @@ namespace gtkpp { _widget = adw_leaflet_new(); } - void Leaflet::append(Widget *widget) { - adw_leaflet_append(ADW_LEAFLET(_widget), widget->gobj()); + void Leaflet::append(const Widget& widget) { + adw_leaflet_append(ADW_LEAFLET(_widget), widget.gobj()); } } diff --git a/gtkpp/Leaflet.h b/gtkpp/Leaflet.h index 380a8d5..1d87b7f 100644 --- a/gtkpp/Leaflet.h +++ b/gtkpp/Leaflet.h @@ -12,7 +12,7 @@ namespace gtkpp { class Leaflet: public Widget { public: Leaflet(); - void append(Widget* widget); + void append(const Widget& widget); }; } diff --git a/gtkpp/ListItemFactory.cpp b/gtkpp/ListItemFactory.cpp new file mode 100644 index 0000000..4e043ea --- /dev/null +++ b/gtkpp/ListItemFactory.cpp @@ -0,0 +1,27 @@ +// +// Created by selim on 13.11.22. +// + +#include "ListItemFactory.h" + +namespace gtkpp { + + void setupListItemCallback(GtkListItemFactory* factory, GtkListItem* list_item, void* data) { + auto self = reinterpret_cast(data); + self->setup(); + } + + ListItemFactory::ListItemFactory() { + _factory = gtk_signal_list_item_factory_new(); + g_signal_connect(_factory, "setup", G_CALLBACK(setupListItemCallback), this); + } + + GtkListItemFactory *ListItemFactory::gobj() const { + return _factory; + } + + void ListItemFactory::setup() { + + } + +} diff --git a/gtkpp/ListItemFactory.h b/gtkpp/ListItemFactory.h new file mode 100644 index 0000000..a63ba51 --- /dev/null +++ b/gtkpp/ListItemFactory.h @@ -0,0 +1,26 @@ +// +// Created by selim on 13.11.22. +// + +#ifndef AUTOCAT_GNOME_LISTITEMFACTORY_H +#define AUTOCAT_GNOME_LISTITEMFACTORY_H + +#include + +namespace gtkpp { + + class ListItemFactory { + private: + GtkListItemFactory* _factory; + + public: + ListItemFactory(); + [[nodiscard]] GtkListItemFactory* gobj() const; + + public: + virtual void setup(); + }; + +} + +#endif //AUTOCAT_GNOME_LISTITEMFACTORY_H diff --git a/gtkpp/ListView.cpp b/gtkpp/ListView.cpp new file mode 100644 index 0000000..11efd07 --- /dev/null +++ b/gtkpp/ListView.cpp @@ -0,0 +1,13 @@ +// +// Created by selim on 13.11.22. +// + +#include "ListView.h" + +namespace gtkpp { + + ListView::ListView(SelectionModel selectionModel, ListItemFactory factory) { + _widget = gtk_list_view_new(selectionModel.gobj(), factory.gobj()); + } + +} diff --git a/gtkpp/ListView.h b/gtkpp/ListView.h new file mode 100644 index 0000000..59a28c7 --- /dev/null +++ b/gtkpp/ListView.h @@ -0,0 +1,21 @@ +// +// Created by selim on 13.11.22. +// + +#ifndef AUTOCAT_GNOME_LISTVIEW_H +#define AUTOCAT_GNOME_LISTVIEW_H + +#include "Widget.h" +#include "SelectionModel.h" +#include "ListItemFactory.h" + +namespace gtkpp { + + class ListView: public Widget { + public: + ListView(SelectionModel selectionModel, ListItemFactory factory); + }; + +} + +#endif //AUTOCAT_GNOME_LISTVIEW_H diff --git a/gtkpp/ScrolledWindow.cpp b/gtkpp/ScrolledWindow.cpp new file mode 100644 index 0000000..e901bf8 --- /dev/null +++ b/gtkpp/ScrolledWindow.cpp @@ -0,0 +1,15 @@ +// +// Created by selim on 13.11.22. +// + +#include "ScrolledWindow.h" + +#include + +namespace gtkpp { + + ScrolledWindow::ScrolledWindow() { + _widget = gtk_scrolled_window_new(); + } + +} diff --git a/gtkpp/ScrolledWindow.h b/gtkpp/ScrolledWindow.h new file mode 100644 index 0000000..56e2892 --- /dev/null +++ b/gtkpp/ScrolledWindow.h @@ -0,0 +1,19 @@ +// +// Created by selim on 13.11.22. +// + +#ifndef AUTOCAT_GNOME_SCROLLEDWINDOW_H +#define AUTOCAT_GNOME_SCROLLEDWINDOW_H + +#include "Widget.h" + +namespace gtkpp { + + class ScrolledWindow: public Widget { + public: + ScrolledWindow(); + }; + +} + +#endif //AUTOCAT_GNOME_SCROLLEDWINDOW_H diff --git a/gtkpp/SelectionModel.cpp b/gtkpp/SelectionModel.cpp new file mode 100644 index 0000000..7990700 --- /dev/null +++ b/gtkpp/SelectionModel.cpp @@ -0,0 +1,26 @@ +// +// Created by selim on 13.11.22. +// + +#include "SelectionModel.h" + +namespace gtkpp { + + SelectionModel::SelectionModel(gtkpp::Selection selection) { + + char *array[] = { "one", "two", "three", "four", NULL }; + GtkStringList *sl = gtk_string_list_new ((const char * const *) array); + + switch (selection) { + case None: + _model = GTK_SELECTION_MODEL(gtk_no_selection_new(G_LIST_MODEL(sl))); + case Single: + _model = GTK_SELECTION_MODEL(gtk_single_selection_new(G_LIST_MODEL(sl))); + } + } + + GtkSelectionModel *SelectionModel::gobj() const { + return _model; + } + +} diff --git a/gtkpp/SelectionModel.h b/gtkpp/SelectionModel.h new file mode 100644 index 0000000..24280b4 --- /dev/null +++ b/gtkpp/SelectionModel.h @@ -0,0 +1,29 @@ +// +// Created by selim on 13.11.22. +// + +#ifndef AUTOCAT_GNOME_SELECTIONMODEL_H +#define AUTOCAT_GNOME_SELECTIONMODEL_H + +#include +#include + +namespace gtkpp { + + enum Selection { + None, + Single + }; + + class SelectionModel { + private: + GtkSelectionModel* _model; + + public: + explicit SelectionModel(Selection selection); + [[nodiscard]] GtkSelectionModel* gobj() const; + }; + +} + +#endif //AUTOCAT_GNOME_SELECTIONMODEL_H diff --git a/gtkpp/Separator.cpp b/gtkpp/Separator.cpp new file mode 100644 index 0000000..05d82a6 --- /dev/null +++ b/gtkpp/Separator.cpp @@ -0,0 +1,9 @@ +// +// Created by selim on 15.11.22. +// + +#include "Separator.h" + +gtkpp::Separator::Separator(GtkOrientation orientation) { + _widget = gtk_separator_new(orientation); +} diff --git a/gtkpp/Separator.h b/gtkpp/Separator.h new file mode 100644 index 0000000..c24d302 --- /dev/null +++ b/gtkpp/Separator.h @@ -0,0 +1,19 @@ +// +// Created by selim on 15.11.22. +// + +#ifndef AUTOCAT_GNOME_SEPARATOR_H +#define AUTOCAT_GNOME_SEPARATOR_H + +#include "Widget.h" + +namespace gtkpp { + + class Separator: public Widget { + public: + explicit Separator(GtkOrientation orientation); + }; + +} + +#endif //AUTOCAT_GNOME_SEPARATOR_H diff --git a/gtkpp/Widget.cpp b/gtkpp/Widget.cpp index 0b25005..3984cbc 100644 --- a/gtkpp/Widget.cpp +++ b/gtkpp/Widget.cpp @@ -19,6 +19,11 @@ namespace gtkpp { _widget = nullptr; } + Widget::Widget(GtkWidget *widget) { + _widget = widget; + g_signal_connect(_widget, "clicked", G_CALLBACK(clickedCallback), this); + } + Widget::Widget(GtkBuilder *builder, const char *id) { _widget = GTK_WIDGET(gtk_builder_get_object(builder, id)); g_signal_connect(_widget, "clicked", G_CALLBACK(clickedCallback), this); @@ -43,6 +48,10 @@ namespace gtkpp { gtk_widget_set_vexpand(_widget, expand); } + void Widget::setHExpand(bool expand) { + gtk_widget_set_hexpand(_widget, expand); + } + void Widget::setHorizontalMargins(int margin) { gtk_widget_set_margin_start(_widget, margin); gtk_widget_set_margin_end(_widget, margin); @@ -61,4 +70,12 @@ namespace gtkpp { g_signal_connect(_widget, "clicked", G_CALLBACK(clickedCallback), this); } + void Widget::show() { + gtk_widget_show(_widget); + } + + void Widget::hide() { + gtk_widget_hide(_widget); + } + } diff --git a/gtkpp/Widget.h b/gtkpp/Widget.h index 88a3836..e0d9ae4 100644 --- a/gtkpp/Widget.h +++ b/gtkpp/Widget.h @@ -28,6 +28,7 @@ namespace gtkpp { public: Widget(); + explicit Widget(GtkWidget* widget); Widget(GtkBuilder* builder, const char* id); [[nodiscard]] GtkWidget* gobj() const; void setMargins(int margin); @@ -35,7 +36,10 @@ namespace gtkpp { void setHorizontalMargins(int margin); void setVAlign(GtkAlign align); void setVExpand(bool expand); + void setHExpand(bool expand); void setEnabled(bool enabled); + void show(); + void hide(); }; } diff --git a/gtkpp/Window.cpp b/gtkpp/Window.cpp index 41a0586..f94942c 100644 --- a/gtkpp/Window.cpp +++ b/gtkpp/Window.cpp @@ -3,6 +3,7 @@ // #include "Window.h" +#include "../App.h" namespace gtkpp { @@ -15,23 +16,28 @@ namespace gtkpp { _builder = nullptr; } - Window::Window(std::shared_ptr app, bool isAppWindow) { + Window::Window(bool isAppWindow): _isAppWindow(isAppWindow) { if(isAppWindow) { - _window = GTK_WINDOW(adw_application_window_new(GTK_APPLICATION(app->gobj()))); + auto app = GTK_APPLICATION(App::instance().gobj()); + _window = GTK_WINDOW(adw_application_window_new(app)); } else { _window = GTK_WINDOW(adw_window_new()); } - _app = app; _builder = nullptr; } - Window::Window(std::shared_ptr app, const char* resourceName, const char* id) { - _app = app; + Window::Window(const char* resourceName, const char* id) { _builder = gtk_builder_new_from_resource(resourceName); _window = GTK_WINDOW(gtk_builder_get_object(_builder, id)); } + Window::~Window() { + if(_window) { + destroy(); + } + } + void Window::show() { gtk_window_present(GTK_WINDOW(_window)); } @@ -48,12 +54,32 @@ namespace gtkpp { gtk_window_set_default_size(GTK_WINDOW(_window), width, height); } - std::shared_ptr Window::application() const { - return _app.lock(); - } - void Window::hide() { gtk_window_close(GTK_WINDOW(_window)); } + void Window::destroy() { + gtk_window_destroy(GTK_WINDOW(_window)); + } + + void Window::setTransientFor(const Window* window) { + gtk_window_set_transient_for(_window, window->gobj()); + } + + void Window::setModal() { + gtk_window_set_modal(_window, true); + } + + void Window::setContent(const Widget& widget) { + if(_isAppWindow) { + adw_application_window_set_content(ADW_APPLICATION_WINDOW(_window), widget.gobj()); + } else { + adw_window_set_content(ADW_WINDOW(_window), widget.gobj()); + } + } + + void Window::setResizable(bool resizable) { + gtk_window_set_resizable(_window, resizable); + } + } diff --git a/gtkpp/Window.h b/gtkpp/Window.h index d64ae64..f00a0bb 100644 --- a/gtkpp/Window.h +++ b/gtkpp/Window.h @@ -6,6 +6,8 @@ #define AUTOCAT_GNOME_WINDOW_H #include "Application.h" +#include "Widget.h" + #include #include @@ -15,21 +17,26 @@ namespace gtkpp { protected: GtkBuilder* _builder; GtkWindow* _window; - std::weak_ptr _app; + bool _isAppWindow; private: friend bool operator==(const Window& wnd1, const Window& wnd2); public: Window(); - Window(std::shared_ptr app, bool isAppWindow); - Window(std::shared_ptr app, const char* resourceName, const char* id); + explicit Window(bool isAppWindow); + Window(const char* resourceName, const char* id); + virtual ~Window(); void show(); void hide(); + void destroy(); void setTitle(const std::string& title); void setDefaultSize(int width, int height); + void setTransientFor(const Window* window); + void setModal(); [[nodiscard]] GtkWindow* gobj() const; - [[nodiscard]] std::shared_ptr application() const; + void setContent(const Widget& widget); + void setResizable(bool resizable); }; using WindowPtr = std::shared_ptr; diff --git a/gui/AddNumberDialog.cpp b/gui/AddNumberDialog.cpp index 3290f0a..4add4ca 100644 --- a/gui/AddNumberDialog.cpp +++ b/gui/AddNumberDialog.cpp @@ -3,21 +3,33 @@ // #include "AddNumberDialog.h" +#include "../services/Api.h" +#include "../services/Storage.h" -AddNumberDialog::AddNumberDialog(GtkWindow* parent): gtkpp::Window() { - auto flags = static_cast(GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL); - auto dialog = gtk_dialog_new_with_buttons("Add plate number", - parent, - flags, - "OK", GTK_RESPONSE_ACCEPT, - "Cancel", GTK_RESPONSE_REJECT, - nullptr); - _window = GTK_WINDOW(dialog); +#include +#include +#include - //auto contentArea = gtk_dialog_get_content_area (GTK_DIALOG(dialog)); - - g_signal_connect_swapped (dialog, - "response", - G_CALLBACK(gtk_window_destroy), - dialog); +AddNumberDialog::AddNumberDialog(gtkpp::Window* parent): gtkpp::Dialog(parent) { + auto content = contentWidget(); + _entry.setMargins(16); + _entry.setPlaceholder("Plate number"); + content.append(_entry); + content.append(_spinner); + _spinner.setMargins(16); + _spinner.hide(); } + +std::string AddNumberDialog::text() const { + return _entry.text(); +} + +cc::result AddNumberDialog::checkNumber() { + _spinner.show(); + _spinner.start(); + std::string text = _entry.text(); + std::transform(text.begin(), text.end(), text.begin(), [](unsigned char c){ return std::toupper(c); }); + auto vehicle = co_await Api::check(text); + Storage::instance().insert(&vehicle); +} + diff --git a/gui/AddNumberDialog.h b/gui/AddNumberDialog.h index 427737b..94517ce 100644 --- a/gui/AddNumberDialog.h +++ b/gui/AddNumberDialog.h @@ -5,11 +5,24 @@ #ifndef AUTOCAT_GNOME_ADDNUMBERDIALOG_H #define AUTOCAT_GNOME_ADDNUMBERDIALOG_H -#include "../gtkpp/Window.h" +#include "../gtkpp/Dialog.h" +#include "../gtkpp/Entry.h" +#include "../gtkpp/Spinner.h" + +#include +#include + +namespace cc = concurrencpp; + +class AddNumberDialog: public gtkpp::Dialog { +private: + gtkpp::Entry _entry; + gtkpp::Spinner _spinner; -class AddNumberDialog: public gtkpp::Window { public: - explicit AddNumberDialog(GtkWindow* parent); + explicit AddNumberDialog(gtkpp::Window* parent); + std::string text() const; + cc::result checkNumber(); }; diff --git a/gui/AddNumberDialog.xml b/gui/AddNumberDialog.xml deleted file mode 100644 index 0e1841f..0000000 --- a/gui/AddNumberDialog.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - _Ok - True - - - - - - - - \ No newline at end of file diff --git a/gui/LoginWindow.cpp b/gui/LoginWindow.cpp index d924c3e..5b47422 100644 --- a/gui/LoginWindow.cpp +++ b/gui/LoginWindow.cpp @@ -5,15 +5,14 @@ #include "LoginWindow.h" #include "MainWindow.h" #include "../services/Api.h" -#include "../coro/GLibMainContextExecutor.h" - #include "../gtkpp/HeaderBar.h" #include "../gtkpp/MessageDialog.h" +#include "../App.h" #include #include -LoginWindow::LoginWindow(std::shared_ptr app): gtkpp::Window(std::move(app), false) { +LoginWindow::LoginWindow(): gtkpp::Window(false) { setDefaultSize(640, 480); @@ -44,7 +43,7 @@ LoginWindow::LoginWindow(std::shared_ptr app): gtkpp::Window rootBox.append(gtkpp::HeaderBar("Login")); rootBox.append(contentBox); - adw_window_set_content(ADW_WINDOW(_window), rootBox.gobj()); + setContent(rootBox); } void LoginWindow::loginClicked() { @@ -56,13 +55,11 @@ void LoginWindow::loginClicked() { try { User user = co_await Api::login(email, password); - if(auto app = this->application()) { - auto mainWindow = std::make_shared(app); - mainWindow->show(); - app->addWindow(mainWindow); - app->removeWindow(this); - hide(); - } + auto mainWindow = std::make_shared(); + mainWindow->show(); + App::instance().addWindow(mainWindow); + App::instance().removeWindow(this); + hide(); } catch (std::exception& ex) { enableControls(true); _spinner.stop(); diff --git a/gui/LoginWindow.h b/gui/LoginWindow.h index 823494b..ef1e103 100644 --- a/gui/LoginWindow.h +++ b/gui/LoginWindow.h @@ -11,7 +11,6 @@ #include "../gtkpp/Entry.h" #include "../gtkpp/Button.h" #include "../gtkpp/Spinner.h" -#include "../gtkpp/Application.h" class LoginWindow: public gtkpp::Window { private: @@ -21,7 +20,7 @@ private: gtkpp::Spinner _spinner; public: - explicit LoginWindow(std::shared_ptr app); + LoginWindow(); void loginClicked(); void validateFields(); diff --git a/gui/MainWindow.cpp b/gui/MainWindow.cpp index 98e5049..f79a839 100644 --- a/gui/MainWindow.cpp +++ b/gui/MainWindow.cpp @@ -4,17 +4,57 @@ #include "MainWindow.h" #include "AddNumberDialog.h" +#include "../gtkpp/MessageDialog.h" +#include "../coro/Coro.h" +#include "../services/Storage.h" #include "../gtkpp/HeaderBar.h" +#include "../gtkpp/Separator.h" #include -MainWindow::MainWindow(std::shared_ptr app): - gtkpp::Window(std::move(app), "/gui/MainWindow.xml", "adw_main_window"), - _addNumberButton(_builder, "add_number_button") { +MainWindow::MainWindow(): gtkpp::Window(true) { + setDefaultSize(640, 480); + setResizable(true); + + _addNumberButton.setIconName("list-add-symbolic"); _addNumberButton.onClick([this] { - std::cout << "Add clicked" << std::endl; - auto dialog = std::make_unique(gobj()); - dialog->show(); + showCheckDialog(); }); + + gtkpp::HeaderBar leftHeader("Vehicles"); + leftHeader.showEndButtons(false); + leftHeader.packStart(_addNumberButton); + + gtkpp::Box leftPanel(GTK_ORIENTATION_VERTICAL, 0); + leftPanel.append(leftHeader); + + gtkpp::HeaderBar rightHeader; + gtkpp::Box rightPanel(GTK_ORIENTATION_VERTICAL, 0); + rightPanel.append(rightHeader); + rightPanel.setHExpand(true); + + _leaflet.append(leftPanel); + _leaflet.append(gtkpp::Separator(GTK_ORIENTATION_VERTICAL)); + _leaflet.append(rightPanel); + _leaflet.setVExpand(true); + + setContent(_leaflet); + + auto vehicles = Storage::instance().select(); + std::cout << "" << std::endl; +} + +void MainWindow::showCheckDialog() { + AddNumberDialog dialog(this); + dialog.show(); + GtkResponseType response = co_await dialog.responseAsync(); + + if(response == GTK_RESPONSE_OK) { + try { + co_await dialog.checkNumber(); + } catch(std::exception& ex) { + gtkpp::MessageDialog::showError(this, ex.what()); + } + } } diff --git a/gui/MainWindow.h b/gui/MainWindow.h index d32b82b..2755af7 100644 --- a/gui/MainWindow.h +++ b/gui/MainWindow.h @@ -15,7 +15,8 @@ private: gtkpp::Button _addNumberButton; public: - explicit MainWindow(std::shared_ptr app); + explicit MainWindow(); + void showCheckDialog(); }; diff --git a/main.cpp b/main.cpp index ea2244d..fdc7ccb 100644 --- a/main.cpp +++ b/main.cpp @@ -3,16 +3,17 @@ #include "services/Settings.h" #include "gtkpp/Application.h" #include "gtkpp/Window.h" +#include "App.h" #include #include -std::shared_ptr createMainWindow(std::shared_ptr app) { +std::shared_ptr createMainWindow() { auto settings = Settings::instance(); if(settings.user().token.empty()) { - return std::make_shared(app); + return std::make_shared(); } else { - return std::make_shared(app); + return std::make_shared(); } } @@ -20,13 +21,13 @@ int main(int argc, char* argv[]) { g_resources_register(g_resource_load("resources.gresource", nullptr)); - auto app = std::make_shared("pro.aliencat.autocat"); + auto app = App::instance(); //std::make_shared("pro.aliencat.autocat"); - app->onActivate([&](){ - auto window = createMainWindow(app); - app->addWindow(window); + app.onActivate([&](){ + auto window = createMainWindow(); + app.addWindow(window); window->show(); }); - return app->run(argc, argv); + return app.run(argc, argv); } diff --git a/models/Engine.cpp b/models/Engine.cpp new file mode 100644 index 0000000..a7d3f9d --- /dev/null +++ b/models/Engine.cpp @@ -0,0 +1,5 @@ +// +// Created by selim on 06.11.22. +// + +#include "Engine.h" diff --git a/models/Engine.h b/models/Engine.h new file mode 100644 index 0000000..dee38f6 --- /dev/null +++ b/models/Engine.h @@ -0,0 +1,21 @@ +// +// Created by selim on 06.11.22. +// + +#ifndef AUTOCAT_GNOME_ENGINE_H +#define AUTOCAT_GNOME_ENGINE_H + +#include + +class Engine { +public: + std::string number; + int volume; + float powerHp; + float powerKw; + std::string fuelType; + + NLOHMANN_DEFINE_TYPE_INTRUSIVE(Engine, number, volume, powerHp, powerKw, fuelType) +}; + +#endif //AUTOCAT_GNOME_ENGINE_H diff --git a/models/JsonOptional.h b/models/JsonOptional.h new file mode 100644 index 0000000..6c2a682 --- /dev/null +++ b/models/JsonOptional.h @@ -0,0 +1,75 @@ +// +// Created by selim on 06.11.22. +// + +#ifndef AUTOCAT_GNOME_JSONOPTIONAL_H +#define AUTOCAT_GNOME_JSONOPTIONAL_H + +#include +#include + +namespace nlohmann { + + template + void optional_to_json(J& j, const char* name, const std::optional& value) + { + if (value) + { + j[name] = *value; + } + } + + template + void optional_from_json(const J& j, const char* name, std::optional& value) + { + const auto it = j.find(name); + if (it != j.end()) + { + if(j.at(name).is_null()) { + value = std::nullopt; + } else { + value = it->template get(); + } + } + else + { + value = std::nullopt; + } + } + + template + constexpr bool is_optional = false; + template + constexpr bool is_optional> = true; + + template + void extended_to_json(const char *key, nlohmann::json &j, const T &value) { + if constexpr (is_optional) + optional_to_json(j, key, value); + else + j[key] = value; + } + template + void extended_from_json(const char *key, const nlohmann::json &j, T &value) { + //std::cout << "parsing key " << key << std::endl; + if constexpr (is_optional) { + optional_from_json(j, key, value); + } else { + j.at(key).get_to(value); + } + } + + #define EXTEND_JSON_TO(v1) extended_to_json(#v1, nlohmann_json_j, nlohmann_json_t.v1); + #define EXTEND_JSON_FROM(v1) extended_from_json(#v1, nlohmann_json_j, nlohmann_json_t.v1); + + #define NLOHMANN_JSONIFY_ALL_THINGS(Type, ...) \ + friend void to_json(nlohmann::json &nlohmann_json_j, const Type &nlohmann_json_t) { \ + NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(EXTEND_JSON_TO, __VA_ARGS__)) \ + } \ + friend void from_json(const nlohmann::json &nlohmann_json_j, Type &nlohmann_json_t) { \ + NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(EXTEND_JSON_FROM, __VA_ARGS__)) \ + } + +} + +#endif //AUTOCAT_GNOME_JSONOPTIONAL_H diff --git a/models/Vehicle.cpp b/models/Vehicle.cpp new file mode 100644 index 0000000..2393f00 --- /dev/null +++ b/models/Vehicle.cpp @@ -0,0 +1,91 @@ +// +// Created by selim on 03.10.22. +// + +#include +#include "Vehicle.h" + +std::string Vehicle::createTableQuery() { + return R"( + CREATE TABLE IF NOT EXISTS vehicles( + number TEXT NOT NULL PRIMARY KEY, + currentNumber TEXT, + color TEXT, + category TEXT, + year INT, + addedDate REAL, + updatedDate REAL, + vin1 TEXT, + vin2 TEXT, + sts TEXT, + pts TEXT, + addedBy TEXT, + engineNumber TEXT, + engineVolume INT, + enginePowerHp REAL, + enginePowerKw REAL, + engineFuelType TEXT + ); + )"; +} + +std::string Vehicle::insertQuery() { + return fmt::format(R"( + INSERT INTO vehicles(number, + currentNumber, + color, + category, + year, + addedDate, + updatedDate, + vin1, vin2, + sts, pts, + addedBy, + engineNumber, + engineVolume, + enginePowerHp, + enginePowerKw, + engineFuelType) + VALUES('{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}') + )", + number, + currentNumber, + color.value_or("NULL"), + category, + year, + addedDate, + updatedDate, + vin1, + vin2.value_or("NULL"), + sts, pts, + addedBy, + engine.number, + engine.volume, + engine.powerHp, + engine.powerKw, + engine.fuelType); +} + +std::string Vehicle::selectQuery() { + return "SELECT * FROM vehicles"; +} + +Vehicle::Vehicle(sqlite3_stmt *stmt) { + number = readString(stmt, 0); + currentNumber = readString(stmt, 1); + color = readStringOptional(stmt, 2); + category = readString(stmt, 3); + year = readInt(stmt, 4); + addedDate = readDouble(stmt, 5); + updatedDate = readDouble(stmt, 6); + vin1 = readString(stmt, 7); + vin2 = readStringOptional(stmt, 8); + sts = readString(stmt, 9); + pts = readString(stmt, 10); + addedBy = readString(stmt, 11); + engine.number = readString(stmt, 12); + engine.volume = readInt(stmt, 13); + engine.powerHp = readDouble(stmt, 14); + engine.powerKw = readDouble(stmt, 15); + engine.fuelType = readString(stmt, 16); +} diff --git a/models/Vehicle.h b/models/Vehicle.h new file mode 100644 index 0000000..2840831 --- /dev/null +++ b/models/Vehicle.h @@ -0,0 +1,42 @@ +// +// Created by selim on 03.10.22. +// + +#ifndef AUTOCAT_GNOME_VEHICLE_H +#define AUTOCAT_GNOME_VEHICLE_H + +#include "../services/IDBEntity.h" +#include "Engine.h" +#include "JsonOptional.h" + +#include +#include + +class Vehicle: public IDBEntity { +public: + std::string number; + std::string currentNumber; + std::optional color; + std::string category; + int year; + double addedDate; + double updatedDate; + std::string vin1; + std::optional vin2; + std::string sts; + std::string pts; + std::string addedBy; + Engine engine; + +public: + Vehicle() = default; + explicit Vehicle(sqlite3_stmt* stmt); + static std::string createTableQuery(); + static std::string selectQuery(); + std::string insertQuery() override; + + NLOHMANN_JSONIFY_ALL_THINGS(Vehicle, number, currentNumber, color, category, year, addedDate, updatedDate, vin1, vin2, sts, pts, addedBy, engine) +}; + + +#endif //AUTOCAT_GNOME_VEHICLE_H diff --git a/models/VehiclesListFactory.cpp b/models/VehiclesListFactory.cpp new file mode 100644 index 0000000..d122bd2 --- /dev/null +++ b/models/VehiclesListFactory.cpp @@ -0,0 +1,9 @@ +// +// Created by selim on 13.11.22. +// + +#include "VehiclesListFactory.h" + +VehiclesListFactory::VehiclesListFactory() { + +} diff --git a/models/VehiclesListFactory.h b/models/VehiclesListFactory.h new file mode 100644 index 0000000..452b2c6 --- /dev/null +++ b/models/VehiclesListFactory.h @@ -0,0 +1,16 @@ +// +// Created by selim on 13.11.22. +// + +#ifndef AUTOCAT_GNOME_VEHICLESLISTFACTORY_H +#define AUTOCAT_GNOME_VEHICLESLISTFACTORY_H + +#include "../gtkpp/ListItemFactory.h" + +class VehiclesListFactory: gtkpp::ListItemFactory { +public: + VehiclesListFactory(); +}; + + +#endif //AUTOCAT_GNOME_VEHICLESLISTFACTORY_H diff --git a/services/Api.cpp b/services/Api.cpp index 8d256eb..7719b6f 100644 --- a/services/Api.cpp +++ b/services/Api.cpp @@ -6,7 +6,7 @@ #include "Settings.h" #include -const std::string Api::_baseUrl = "https://vps.aliencat.pro:8443/"; +const std::string Api::_baseUrl = "http://127.0.0.1:3000/"; //"https://vps.aliencat.pro:8443/"; SoupSession* Api::_session = soup_session_new(); template @@ -36,6 +36,12 @@ cc::result Api::post(const std::string &method, const nlohmann::json& params) std::string url = _baseUrl + method; auto msg = soup_message_new(SOUP_METHOD_POST, url.c_str()); + auto token = Settings::instance().user().token; + if(!token.empty()) { + auto authHeader = "Bearer " + token; + soup_message_headers_append(msg->request_headers, "Authorization", authHeader.c_str()); + } + auto promise = new cc::result_promise(); auto result = promise->get_result(); @@ -67,3 +73,12 @@ cc::result Api::login(std::string email, std::string password) { co_return user; } + +cc::result Api::check(const std::string& number, bool force) { + nlohmann::json params = { + { "number", number }, + { "forceUpdate", force } + }; + + return post("vehicles/check", params); +} diff --git a/services/Api.h b/services/Api.h index 6071ce0..0baaeb8 100644 --- a/services/Api.h +++ b/services/Api.h @@ -11,6 +11,7 @@ #include #include "../models/User.h" +#include "../models/Vehicle.h" #include "../coro/Coro.h" namespace cc = concurrencpp; @@ -26,6 +27,7 @@ private: public: static cc::result login(std::string email, std::string password); + static cc::result check(const std::string& number, bool force = false); }; diff --git a/services/IDBEntity.cpp b/services/IDBEntity.cpp new file mode 100644 index 0000000..832e3ec --- /dev/null +++ b/services/IDBEntity.cpp @@ -0,0 +1,41 @@ +// +// Created by selim on 09.11.22. +// + +#include "IDBEntity.h" + +std::string IDBEntity::readString(sqlite3_stmt *stmt, int column) { + return { reinterpret_cast(sqlite3_column_text(stmt, column)) }; +} + +int IDBEntity::readInt(sqlite3_stmt *stmt, int column) { + return sqlite3_column_int(stmt, column); +} + +double IDBEntity::readDouble(sqlite3_stmt *stmt, int column) { + return sqlite3_column_double(stmt, column); +} + +StringOptional IDBEntity::readStringOptional(sqlite3_stmt *stmt, int column) { + if(sqlite3_column_type(stmt, column) == SQLITE_NULL) { + return std::nullopt; + } else { + return readString(stmt, column); + } +} + +IntOptional IDBEntity::readIntOptional(sqlite3_stmt *stmt, int column) { + if(sqlite3_column_type(stmt, column) == SQLITE_NULL) { + return std::nullopt; + } else { + return readInt(stmt, column); + } +} + +DoubleOptional IDBEntity::readDoubleOptional(sqlite3_stmt *stmt, int column) { + if(sqlite3_column_type(stmt, column) == SQLITE_NULL) { + return std::nullopt; + } else { + return readDouble(stmt, column); + } +} diff --git a/services/IDBEntity.h b/services/IDBEntity.h new file mode 100644 index 0000000..174802e --- /dev/null +++ b/services/IDBEntity.h @@ -0,0 +1,31 @@ +// +// Created by selim on 06.11.22. +// + +#ifndef AUTOCAT_GNOME_IDBENTITY_H +#define AUTOCAT_GNOME_IDBENTITY_H + +#include +#include +#include +#include +#include + +using StringOptional = std::optional; +using IntOptional = std::optional; +using DoubleOptional = std::optional; + +struct IDBEntity { +public: + virtual std::string insertQuery() = 0; + +protected: + static std::string readString(sqlite3_stmt* stmt, int column); + static int readInt(sqlite3_stmt* stmt, int column); + static double readDouble(sqlite3_stmt* stmt, int column); + static StringOptional readStringOptional(sqlite3_stmt* stmt, int column); + static IntOptional readIntOptional(sqlite3_stmt* stmt, int column); + static DoubleOptional readDoubleOptional(sqlite3_stmt* stmt, int column); +}; + +#endif //AUTOCAT_GNOME_IDBENTITY_H diff --git a/services/Settings.cpp b/services/Settings.cpp index 1a4dd9c..3457df9 100644 --- a/services/Settings.cpp +++ b/services/Settings.cpp @@ -9,17 +9,15 @@ #include #include -namespace fs = std::filesystem; - -fs::path getDataPath() { +fs::path Settings::getDataPath() { auto dataDir = std::getenv("XDG_DATA_HOME"); if(dataDir) { - return fs::path(dataDir); + return fs::path(dataDir) / "autocat"; } auto home = std::getenv("HOME"); if(home) { - return fs::path(home) / ".local" / "share"; + return fs::path(home) / ".local" / "share" / "autocat"; } throw std::runtime_error("Failed to find data home directory"); @@ -46,7 +44,7 @@ Settings Settings::instance() { } Settings::Settings() { - _dataPath = getDataPath() / "autocat"; + _dataPath = getDataPath(); if(!fs::exists(_dataPath)) { fs::create_directories(_dataPath); } diff --git a/services/Settings.h b/services/Settings.h index f0f743d..11debff 100644 --- a/services/Settings.h +++ b/services/Settings.h @@ -8,6 +8,8 @@ #include "../models/User.h" #include +namespace fs = std::filesystem; + class Settings { private: std::filesystem::path _dataPath; @@ -15,6 +17,7 @@ private: public: static Settings instance(); Settings(); + static fs::path getDataPath(); [[nodiscard]] User user() const; void setUser(const User& user); diff --git a/services/Storage.cpp b/services/Storage.cpp new file mode 100644 index 0000000..e95019e --- /dev/null +++ b/services/Storage.cpp @@ -0,0 +1,42 @@ +// +// Created by selim on 25.10.22. +// + +#include "Storage.h" +#include "Settings.h" + +#include + +Storage Storage::instance() { + static Storage instance; + return instance; +} + +Storage::Storage(): _sqlite(nullptr) { + auto path = Settings::getDataPath() / "vehicles.sqlite"; + int result = sqlite3_open(path.c_str(), &_sqlite); + if(result != SQLITE_OK) { + throw std::runtime_error(sqlite3_errmsg(_sqlite)); + } + + executeQuery(Vehicle::createTableQuery()); +} + +Storage::~Storage() { + if(_sqlite) { + sqlite3_close(_sqlite); + } +} + +void Storage::executeQuery(const std::string& query) { + char* error = nullptr; + sqlite3_exec(_sqlite, query.c_str(), nullptr, nullptr, &error); + if(error) { + throw std::runtime_error(error); + // TODO: error buffer should be deallocated with sqlite3_free() + } +} + +void Storage::insert(IDBEntity *entity) { + executeQuery(entity->insertQuery()); +} diff --git a/services/Storage.h b/services/Storage.h new file mode 100644 index 0000000..4a792b5 --- /dev/null +++ b/services/Storage.h @@ -0,0 +1,64 @@ +// +// Created by selim on 25.10.22. +// + +#ifndef AUTOCAT_GNOME_STORAGE_H +#define AUTOCAT_GNOME_STORAGE_H + +#include "../models/Vehicle.h" +#include "IDBEntity.h" + +#include +#include + +class Storage { +private: + sqlite3* _sqlite; + +private: + void executeQuery(const std::string& query); + +public: + Storage(); + ~Storage(); + static Storage instance(); + void insert(IDBEntity* entity); + + template + std::vector select() { + auto sql = T::selectQuery(); + sqlite3_stmt* stmt = nullptr; + std::vector rows; + + int result = sqlite3_prepare_v2(_sqlite, sql.c_str(), sql.size(), &stmt, nullptr); + if(result != SQLITE_OK) { + throw std::runtime_error(sqlite3_errmsg(_sqlite)); + } + + do { + result = sqlite3_step(stmt); + switch(result) { + case SQLITE_BUSY: + // TODO: Rollback transaction + break; + case SQLITE_ERROR: + throw std::runtime_error(sqlite3_errmsg(_sqlite)); + case SQLITE_ROW: + rows.emplace_back(stmt); + break; + default: + break; + } + } while (result == SQLITE_ROW); + + result = sqlite3_finalize(stmt); + if(result != SQLITE_OK) { + throw std::runtime_error(sqlite3_errmsg(_sqlite)); + } + + return rows; + } +}; + + +#endif //AUTOCAT_GNOME_STORAGE_H