Massive refactoring

This commit is contained in:
Selim Mustafaev 2022-11-21 23:25:52 +03:00
parent 4814d9002d
commit 9d054d4119
53 changed files with 1004 additions and 120 deletions

10
App.cpp Normal file
View File

@ -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;
}

17
App.h Normal file
View File

@ -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

View File

@ -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)

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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());
}
}

View File

@ -15,6 +15,7 @@ namespace gtkpp {
using Widget::Widget;
Button();
void setTitle(const std::string& title);
void setIconName(const std::string& name);
};
}

View File

@ -3,3 +3,58 @@
//
#include "Dialog.h"
namespace gtkpp {
void responseCallback(GtkDialog*, GtkResponseType responseId, void* data) {
auto dialog = reinterpret_cast<Dialog*>(data);
dialog->_response = responseId;
dialog->_signalResponse.emit(responseId);
}
Dialog::Dialog(Window* parent) {
auto flags = static_cast<GtkDialogFlags>(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<void(GtkResponseType)> &callback) {
_signalResponse.connect(callback);
}
cc::result<GtkResponseType> Dialog::responseAsync() {
auto promise = std::make_shared<cc::result_promise<GtkResponseType>>();
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};
}
}

View File

@ -6,12 +6,30 @@
#define AUTOCAT_GNOME_DIALOG_H
#include "Window.h"
#include "Box.h"
#include <sigc++/sigc++.h>
#include <concurrencpp/concurrencpp.h>
namespace cc = concurrencpp;
namespace gtkpp {
class Dialog: public Window {
private:
GtkResponseType _response = GTK_RESPONSE_NONE;
gulong _responseHandlerId;
sigc::signal<void(GtkResponseType)> _signalResponse;
friend void responseCallback(GtkDialog*, GtkResponseType responseId, void* data);
public:
using Window::Window;
void onResponse(const std::function<void(GtkResponseType)>& callback);
public:
explicit Dialog(Window* parent);
~Dialog();
cc::result<GtkResponseType> responseAsync();
GtkResponseType currentResponse() const;
Box contentWidget() const;
};
}

View File

@ -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) {

View File

@ -13,6 +13,7 @@ namespace gtkpp {
class Entry: public Widget {
private:
gulong _changedHandlerId;
sigc::signal<void()> _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);

View File

@ -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());
}
}

View File

@ -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);
};
}

View File

@ -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());
}
}

View File

@ -12,7 +12,7 @@ namespace gtkpp {
class Leaflet: public Widget {
public:
Leaflet();
void append(Widget* widget);
void append(const Widget& widget);
};
}

27
gtkpp/ListItemFactory.cpp Normal file
View File

@ -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<ListItemFactory*>(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() {
}
}

26
gtkpp/ListItemFactory.h Normal file
View File

@ -0,0 +1,26 @@
//
// Created by selim on 13.11.22.
//
#ifndef AUTOCAT_GNOME_LISTITEMFACTORY_H
#define AUTOCAT_GNOME_LISTITEMFACTORY_H
#include <gtk/gtk.h>
namespace gtkpp {
class ListItemFactory {
private:
GtkListItemFactory* _factory;
public:
ListItemFactory();
[[nodiscard]] GtkListItemFactory* gobj() const;
public:
virtual void setup();
};
}
#endif //AUTOCAT_GNOME_LISTITEMFACTORY_H

13
gtkpp/ListView.cpp Normal file
View File

@ -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());
}
}

21
gtkpp/ListView.h Normal file
View File

@ -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

15
gtkpp/ScrolledWindow.cpp Normal file
View File

@ -0,0 +1,15 @@
//
// Created by selim on 13.11.22.
//
#include "ScrolledWindow.h"
#include <gtk/gtkscrolledwindow.h>
namespace gtkpp {
ScrolledWindow::ScrolledWindow() {
_widget = gtk_scrolled_window_new();
}
}

19
gtkpp/ScrolledWindow.h Normal file
View File

@ -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

26
gtkpp/SelectionModel.cpp Normal file
View File

@ -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;
}
}

29
gtkpp/SelectionModel.h Normal file
View File

@ -0,0 +1,29 @@
//
// Created by selim on 13.11.22.
//
#ifndef AUTOCAT_GNOME_SELECTIONMODEL_H
#define AUTOCAT_GNOME_SELECTIONMODEL_H
#include <gtk/gtk.h>
#include <gio/glistmodel.h>
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

9
gtkpp/Separator.cpp Normal file
View File

@ -0,0 +1,9 @@
//
// Created by selim on 15.11.22.
//
#include "Separator.h"
gtkpp::Separator::Separator(GtkOrientation orientation) {
_widget = gtk_separator_new(orientation);
}

19
gtkpp/Separator.h Normal file
View File

@ -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

View File

@ -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);
}
}

View File

@ -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();
};
}

View File

@ -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<Application> 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<Application> 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<Application> 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);
}
}

View File

@ -6,6 +6,8 @@
#define AUTOCAT_GNOME_WINDOW_H
#include "Application.h"
#include "Widget.h"
#include <adwaita.h>
#include <memory>
@ -15,21 +17,26 @@ namespace gtkpp {
protected:
GtkBuilder* _builder;
GtkWindow* _window;
std::weak_ptr<Application> _app;
bool _isAppWindow;
private:
friend bool operator==(const Window& wnd1, const Window& wnd2);
public:
Window();
Window(std::shared_ptr<Application> app, bool isAppWindow);
Window(std::shared_ptr<Application> 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> application() const;
void setContent(const Widget& widget);
void setResizable(bool resizable);
};
using WindowPtr = std::shared_ptr<Window>;

View File

@ -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<GtkDialogFlags>(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 <iostream>
#include <algorithm>
#include <cctype>
//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<void> 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);
}

View File

@ -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 <concurrencpp/concurrencpp.h>
#include <vector>
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<void> checkNumber();
};

View File

@ -1,32 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<object class="GtkDialog" id="add_number_dialog">
<!--
<child type="action">
<object class="GtkButton" id="button_cancel"/>
</child>
<child type="action">
<object class="GtkButton" id="button_ok">
</object>
</child>
<action-widgets>
<action-widget response="cancel">button_cancel</action-widget>
<action-widget response="ok" default="true">button_ok</action-widget>
</action-widgets>
-->
<child internal-child="content_area">
<object class="GtkBox" id="vbox1">
<child internal-child="action_area">
<object class="GtkBox" id="hbuttonbox1">
<child>
<object class="GtkButton" id="ok_button">
<property name="label" translatable="yes">_Ok</property>
<property name="use-underline">True</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</interface>

View File

@ -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 <iostream>
#include <utility>
LoginWindow::LoginWindow(std::shared_ptr<gtkpp::Application> app): gtkpp::Window(std::move(app), false) {
LoginWindow::LoginWindow(): gtkpp::Window(false) {
setDefaultSize(640, 480);
@ -44,7 +43,7 @@ LoginWindow::LoginWindow(std::shared_ptr<gtkpp::Application> 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<MainWindow>(app);
mainWindow->show();
app->addWindow(mainWindow);
app->removeWindow(this);
hide();
}
auto mainWindow = std::make_shared<MainWindow>();
mainWindow->show();
App::instance().addWindow(mainWindow);
App::instance().removeWindow(this);
hide();
} catch (std::exception& ex) {
enableControls(true);
_spinner.stop();

View File

@ -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<gtkpp::Application> app);
LoginWindow();
void loginClicked();
void validateFields();

View File

@ -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 <iostream>
MainWindow::MainWindow(std::shared_ptr<gtkpp::Application> 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<AddNumberDialog>(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<Vehicle>();
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());
}
}
}

View File

@ -15,7 +15,8 @@ private:
gtkpp::Button _addNumberButton;
public:
explicit MainWindow(std::shared_ptr<gtkpp::Application> app);
explicit MainWindow();
void showCheckDialog();
};

View File

@ -3,16 +3,17 @@
#include "services/Settings.h"
#include "gtkpp/Application.h"
#include "gtkpp/Window.h"
#include "App.h"
#include <memory>
#include <iostream>
std::shared_ptr<gtkpp::Window> createMainWindow(std::shared_ptr<gtkpp::Application> app) {
std::shared_ptr<gtkpp::Window> createMainWindow() {
auto settings = Settings::instance();
if(settings.user().token.empty()) {
return std::make_shared<LoginWindow>(app);
return std::make_shared<LoginWindow>();
} else {
return std::make_shared<MainWindow>(app);
return std::make_shared<MainWindow>();
}
}
@ -20,13 +21,13 @@ int main(int argc, char* argv[]) {
g_resources_register(g_resource_load("resources.gresource", nullptr));
auto app = std::make_shared<gtkpp::Application>("pro.aliencat.autocat");
auto app = App::instance(); //std::make_shared<gtkpp::Application>("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);
}

5
models/Engine.cpp Normal file
View File

@ -0,0 +1,5 @@
//
// Created by selim on 06.11.22.
//
#include "Engine.h"

21
models/Engine.h Normal file
View File

@ -0,0 +1,21 @@
//
// Created by selim on 06.11.22.
//
#ifndef AUTOCAT_GNOME_ENGINE_H
#define AUTOCAT_GNOME_ENGINE_H
#include <nlohmann/json.hpp>
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

75
models/JsonOptional.h Normal file
View File

@ -0,0 +1,75 @@
//
// Created by selim on 06.11.22.
//
#ifndef AUTOCAT_GNOME_JSONOPTIONAL_H
#define AUTOCAT_GNOME_JSONOPTIONAL_H
#include <nlohmann/json.hpp>
#include <iostream>
namespace nlohmann {
template<class J, class T>
void optional_to_json(J& j, const char* name, const std::optional<T>& value)
{
if (value)
{
j[name] = *value;
}
}
template<class J, class T>
void optional_from_json(const J& j, const char* name, std::optional<T>& 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<T>();
}
}
else
{
value = std::nullopt;
}
}
template <typename>
constexpr bool is_optional = false;
template <typename T>
constexpr bool is_optional<std::optional<T>> = true;
template <typename T>
void extended_to_json(const char *key, nlohmann::json &j, const T &value) {
if constexpr (is_optional<T>)
optional_to_json(j, key, value);
else
j[key] = value;
}
template <typename T>
void extended_from_json(const char *key, const nlohmann::json &j, T &value) {
//std::cout << "parsing key " << key << std::endl;
if constexpr (is_optional<T>) {
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

91
models/Vehicle.cpp Normal file
View File

@ -0,0 +1,91 @@
//
// Created by selim on 03.10.22.
//
#include <fmt/core.h>
#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);
}

42
models/Vehicle.h Normal file
View File

@ -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 <optional>
#include <sqlite3.h>
class Vehicle: public IDBEntity {
public:
std::string number;
std::string currentNumber;
std::optional<std::string> color;
std::string category;
int year;
double addedDate;
double updatedDate;
std::string vin1;
std::optional<std::string> 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

View File

@ -0,0 +1,9 @@
//
// Created by selim on 13.11.22.
//
#include "VehiclesListFactory.h"
VehiclesListFactory::VehiclesListFactory() {
}

View File

@ -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

View File

@ -6,7 +6,7 @@
#include "Settings.h"
#include <memory>
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<typename T>
@ -36,6 +36,12 @@ cc::result<T> 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<T>();
auto result = promise->get_result();
@ -67,3 +73,12 @@ cc::result<User> Api::login(std::string email, std::string password) {
co_return user;
}
cc::result<Vehicle> Api::check(const std::string& number, bool force) {
nlohmann::json params = {
{ "number", number },
{ "forceUpdate", force }
};
return post<Vehicle>("vehicles/check", params);
}

View File

@ -11,6 +11,7 @@
#include <concurrencpp/concurrencpp.h>
#include "../models/User.h"
#include "../models/Vehicle.h"
#include "../coro/Coro.h"
namespace cc = concurrencpp;
@ -26,6 +27,7 @@ private:
public:
static cc::result<User> login(std::string email, std::string password);
static cc::result<Vehicle> check(const std::string& number, bool force = false);
};

41
services/IDBEntity.cpp Normal file
View File

@ -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<const char*>(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);
}
}

31
services/IDBEntity.h Normal file
View File

@ -0,0 +1,31 @@
//
// Created by selim on 06.11.22.
//
#ifndef AUTOCAT_GNOME_IDBENTITY_H
#define AUTOCAT_GNOME_IDBENTITY_H
#include <string>
#include <optional>
#include <variant>
#include <vector>
#include <sqlite3.h>
using StringOptional = std::optional<std::string>;
using IntOptional = std::optional<int>;
using DoubleOptional = std::optional<double>;
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

View File

@ -9,17 +9,15 @@
#include <iostream>
#include <fstream>
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);
}

View File

@ -8,6 +8,8 @@
#include "../models/User.h"
#include <filesystem>
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);

42
services/Storage.cpp Normal file
View File

@ -0,0 +1,42 @@
//
// Created by selim on 25.10.22.
//
#include "Storage.h"
#include "Settings.h"
#include <fmt/core.h>
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());
}

64
services/Storage.h Normal file
View File

@ -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 <utility>
#include <sqlite3.h>
class Storage {
private:
sqlite3* _sqlite;
private:
void executeQuery(const std::string& query);
public:
Storage();
~Storage();
static Storage instance();
void insert(IDBEntity* entity);
template<typename T>
std::vector<T> select() {
auto sql = T::selectQuery();
sqlite3_stmt* stmt = nullptr;
std::vector<T> 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