commit 51144e1854d63aedfe823df9f9d0d7987211d212 Author: Tobias Krause Date: Fri Jan 27 22:32:07 2023 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..774aef0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,46 @@ +.vscode/ +flashcards + +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb diff --git a/README.md b/README.md new file mode 100644 index 0000000..b80ee6a --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# Flashcards diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..af8ab37 --- /dev/null +++ b/meson.build @@ -0,0 +1,19 @@ +project('flashcards', 'c', version : '1.0') + +gtk_dep = dependency('gtk4') +libadwaita_dep = dependency('libadwaita-1', version: '>=1.2') +sqlite_dep = dependency('sqlite3') + +gnome = import('gnome') + +subdir('po') +subdir('src') +subdir('resources') + +executable('flashcards', + sourcefiles, + resources, + schemas, + dependencies: [gtk_dep, libadwaita_dep, sqlite_dep], + install: true +) diff --git a/po/LINGUAS b/po/LINGUAS new file mode 100644 index 0000000..150b83b --- /dev/null +++ b/po/LINGUAS @@ -0,0 +1,2 @@ +en +de \ No newline at end of file diff --git a/po/POTFILES b/po/POTFILES new file mode 100644 index 0000000..39b8c77 --- /dev/null +++ b/po/POTFILES @@ -0,0 +1,4 @@ +resources/window.blp +src/flashcardsapp.c +src/flashcardsappwin.c +src/database.c \ No newline at end of file diff --git a/po/de.po b/po/de.po new file mode 100644 index 0000000..eb13700 --- /dev/null +++ b/po/de.po @@ -0,0 +1,15 @@ +#: resources/window.blp:5 src/flashcardsappwin.c:28 +msgid "Flashcards" +msgstr "Karteikarten" + +#: resources/window.blp:20 +msgid "Topics" +msgstr "Themen" + +#: resources/window.blp:94 +msgid "Upgrade Assistant" +msgstr "" + +#: resources/window.blp:98 +msgid "About Flashcards" +msgstr "Über Karteikarten" diff --git a/po/en.po b/po/en.po new file mode 100644 index 0000000..9c823d5 --- /dev/null +++ b/po/en.po @@ -0,0 +1,15 @@ +#: resources/window.blp:5 src/flashcardsappwin.c:28 +msgid "Flashcards" +msgstr "Flashcards" + +#: resources/window.blp:20 +msgid "Topics" +msgstr "Topics" + +#: resources/window.blp:94 +msgid "Upgrade Assistant" +msgstr "" + +#: resources/window.blp:98 +msgid "About Flashcards" +msgstr "About Flashcards" diff --git a/po/flashcards.pot b/po/flashcards.pot new file mode 100644 index 0000000..ee356c8 --- /dev/null +++ b/po/flashcards.pot @@ -0,0 +1,34 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the flashcards package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: flashcards\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-11-28 22:18+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: resources/window.blp:5 src/flashcardsappwin.c:28 +msgid "Flashcards" +msgstr "" + +#: resources/window.blp:20 +msgid "Topics" +msgstr "" + +#: resources/window.blp:94 +msgid "Upgrade Assistant" +msgstr "" + +#: resources/window.blp:98 +msgid "About Flashcards" +msgstr "" diff --git a/po/meson.build b/po/meson.build new file mode 100644 index 0000000..b9d2d48 --- /dev/null +++ b/po/meson.build @@ -0,0 +1,3 @@ +i18n = import('i18n') + +i18n.gettext(meson.project_name(), preset: 'glib') \ No newline at end of file diff --git a/resources/flashcards.gresource.xml b/resources/flashcards.gresource.xml new file mode 100644 index 0000000..64c6155 --- /dev/null +++ b/resources/flashcards.gresource.xml @@ -0,0 +1,6 @@ + + + + window.ui + + diff --git a/resources/meson.build b/resources/meson.build new file mode 100644 index 0000000..efbb4ff --- /dev/null +++ b/resources/meson.build @@ -0,0 +1,15 @@ +blueprints = custom_target('blueprints', + input: files( + 'window.blp' + ), + output: '.', + command: [find_program('blueprint-compiler'), 'batch-compile', '@OUTPUT@', '@CURRENT_SOURCE_DIR@', '@INPUT@'], +) + +resources = gnome.compile_resources('resources', + 'flashcards.gresource.xml', + dependencies: blueprints, + c_name: 'flashcards' +) + +schemas = gnome.compile_schemas() \ No newline at end of file diff --git a/resources/window.blp b/resources/window.blp new file mode 100644 index 0000000..a1a61a5 --- /dev/null +++ b/resources/window.blp @@ -0,0 +1,102 @@ +using Gtk 4.0; +using Adw 1; + +template FlashcardsAppWindow : Adw.ApplicationWindow { + title: _("Flashcards"); + + Adw.Leaflet leaflet { + can-navigate-back: true; + + Box { + orientation: vertical; + hexpand: true; + width-request: 360; + + Adw.HeaderBar { + show-end-title-buttons: bind leaflet.folded; + + [title] + Adw.WindowTitle { + title: _("Topics"); + } + } + + Adw.Clamp { + hexpand: true; + child: ListBox topics { + hexpand: true; + selection-mode: single; + margin-top: 12; + margin-bottom: 12; + margin-start: 12; + margin-end: 12; + row-selected => on_category_selected(); + + styles [ + "boxed-list", + ] + }; + } + } + + Adw.LeafletPage { + navigatable: false; + child: Separator {}; + } + + Box { + orientation: vertical; + hexpand: true; + + Adw.HeaderBar { + show-start-title-buttons: bind leaflet.folded; + Button leaflet_previous { + visible: bind leaflet.folded; + icon-name: "go-previous-symbolic"; + clicked => on_navigate_back(); + } + + [end] + Gtk.MenuButton { + icon-name: "open-menu-symbolic"; + menu-model: primary_menu; + } + } + + Adw.Clamp { + hexpand: true; + child: Adw.Bin { + margin-top: 12; + margin-bottom: 12; + margin-start: 12; + margin-end: 12; + + styles [ + "card", + ] + child: Label { + margin-top: 24; + margin-bottom: 24; + margin-start: 24; + margin-end: 24; + wrap: true; + label: "Wie viel Grad hat ein Kreis?"; + }; + }; + } + } + } +} + +menu primary_menu { + section { + item { + label: _("Upgrade Assistant"); + action: "win.show-upgrade-assistant"; + } + item { + label: _("About Flashcards"); + action: "app.about"; + } + } +} \ No newline at end of file diff --git a/src/database.c b/src/database.c new file mode 100644 index 0000000..1603834 --- /dev/null +++ b/src/database.c @@ -0,0 +1,133 @@ +#include "database.h" + +#include +#include + +sqlite3 *db; + +void database_connect(const char *path) +{ + // char *zErrMsg = 0; + + int rc; + + gchar *file = g_build_filename(path, "cards.db", NULL); + rc = sqlite3_open(file, &db); + + if (rc) + { + fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db)); + return; + } + else + { + fprintf(stderr, "Opened database successfully\n"); + } + g_free(file); +} + +void database_close() +{ + sqlite3_close(db); +} + +void database_create_tables() +{ + int rc; + sqlite3_stmt *stmt; + + char *sql = "CREATE TABLE IF NOT EXISTS `cards` (" + "`category` INTEGER NOT NULL," + "`task` TEXT NOT NULL," + "`solution` TEXT NOT NULL" + ")"; + + rc = sqlite3_prepare_v2(db, sql, -1, &stmt, 0); + + if (rc != SQLITE_OK) + { + fprintf(stderr, "Failed to execute statement: %s\n", sqlite3_errmsg(db)); + } + + sqlite3_step(stmt); + sqlite3_finalize(stmt); + + sql = "CREATE TABLE IF NOT EXISTS `categories` (" + "`id` INTEGER NOT NULL," + "`name` TEXT NOT NULL," + "PRIMARY KEY(`id` AUTOINCREMENT)" + ")"; + + rc = sqlite3_prepare_v2(db, sql, -1, &stmt, 0); + + if (rc != SQLITE_OK) + { + fprintf(stderr, "Failed to execute statement: %s\n", sqlite3_errmsg(db)); + } + + sqlite3_step(stmt); + sqlite3_finalize(stmt); +} + +void database_save_category() +{ +} + +GArray *database_load_categories() +{ + GArray *categories = g_array_new(TRUE, FALSE, sizeof(category)); + + int rc; + sqlite3_stmt *stmt; + + rc = sqlite3_prepare_v2(db, "SELECT * FROM categories", -1, &stmt, 0); + + if (rc != SQLITE_OK) + { + fprintf(stderr, "Failed to execute statement: %s\n", sqlite3_errmsg(db)); + } + + while (sqlite3_step(stmt) == SQLITE_ROW) + { + int id = sqlite3_column_int(stmt, 0); + const unsigned char *temp_name = sqlite3_column_text(stmt, 1); + + char *name = g_new0(char, strlen(temp_name)); + strcpy(name, temp_name); + category c = {id, name}; + g_array_append_val(categories, c); + } + + sqlite3_finalize(stmt); + return categories; +} + +void database_save_card(card c) +{ + int rc; + sqlite3_stmt *stmt; + + fprintf(stdout, "%s: %s\n", c.task, c.solution); + rc = sqlite3_prepare_v2(db, "INSERT INTO cards VALUES(?, ?, ?)", -1, &stmt, 0); + + if (rc == SQLITE_OK) + { + sqlite3_bind_int(stmt, 0, c.category); + sqlite3_bind_text(stmt, 1, c.task, -1, SQLITE_STATIC); + sqlite3_bind_text(stmt, 2, c.solution, -1, SQLITE_STATIC); + } + else + { + fprintf(stderr, "Failed to execute statement: %s\n", sqlite3_errmsg(db)); + } + + sqlite3_step(stmt); + sqlite3_finalize(stmt); +} + +GArray *database_load_cards() +{ + GArray *cards = g_array_new(TRUE, FALSE, sizeof(card)); + + return cards; +} \ No newline at end of file diff --git a/src/database.h b/src/database.h new file mode 100644 index 0000000..d0cdd76 --- /dev/null +++ b/src/database.h @@ -0,0 +1,28 @@ +#include + +typedef struct category +{ + int id; + char *name; +} category; + +typedef struct card +{ + int category; + char *task; + char *solution; +} card; + +void database_connect(const char *path); + +void database_close(); + +void database_create_tables(); + +void database_save_category(); + +GArray *database_load_categories(); + +void database_save_card(card c); + +GArray *database_load_cards(); \ No newline at end of file diff --git a/src/flashcardsapp.c b/src/flashcardsapp.c new file mode 100644 index 0000000..1fdbdf4 --- /dev/null +++ b/src/flashcardsapp.c @@ -0,0 +1,65 @@ +#include +#include + +#include "flashcardsapp.h" +#include "flashcardsappwin.h" + +#include "database.h" + +struct _FlashcardsApp +{ + AdwApplication parent; +}; + +G_DEFINE_TYPE(FlashcardsApp, flashcards_app, ADW_TYPE_APPLICATION); + +static void +flashcards_app_init(FlashcardsApp *app) +{ +} + +static void +flashcards_app_activate(GApplication *app) +{ + FlashcardsAppWindow *win; + win = flashcards_app_window_new(FLASHCARDS_APP(app)); + gtk_window_present(GTK_WINDOW(win)); +} + +static void +flashcards_app_open(GApplication *app, + GFile **files, + int n_files, + const char *hint) +{ + GList *windows; + FlashcardsAppWindow *win; + int i; + + windows = gtk_application_get_windows(GTK_APPLICATION(app)); + if (windows) + win = FLASHCARDS_APP_WINDOW(windows->data); + else + win = flashcards_app_window_new(FLASHCARDS_APP(app)); + + for (i = 0; i < n_files; i++) + flashcards_app_window_open(win, files[i]); + + gtk_window_present(GTK_WINDOW(win)); +} + +static void +flashcards_app_class_init(FlashcardsAppClass *class) +{ + G_APPLICATION_CLASS(class)->activate = flashcards_app_activate; + G_APPLICATION_CLASS(class)->open = flashcards_app_open; +} + +FlashcardsApp * +flashcards_app_new(void) +{ + return g_object_new(FLASHCARDS_APP_TYPE, + "application-id", "li.sopht.flashcards", + "flags", G_APPLICATION_HANDLES_OPEN, + NULL); +} diff --git a/src/flashcardsapp.h b/src/flashcardsapp.h new file mode 100644 index 0000000..6344ecb --- /dev/null +++ b/src/flashcardsapp.h @@ -0,0 +1,12 @@ +#ifndef __FLASHCARDSAPP_H +#define __FLASHCARDSAPP_H + +#include +#include + +#define FLASHCARDS_APP_TYPE (flashcards_app_get_type ()) +G_DECLARE_FINAL_TYPE (FlashcardsApp, flashcards_app, FLASHCARDS, APP, AdwApplication) + +FlashcardsApp *flashcards_app_new (void); + +#endif /* __FLASHCARDSAPP_H */ diff --git a/src/flashcardsappwin.c b/src/flashcardsappwin.c new file mode 100644 index 0000000..3573ca6 --- /dev/null +++ b/src/flashcardsappwin.c @@ -0,0 +1,106 @@ +#include +#include + +#include + +#include "flashcardsapp.h" +#include "flashcardsappwin.h" + +#include "database.h" + +struct _FlashcardsAppWindow +{ + AdwApplicationWindow parent; + + GArray *categories; + + AdwLeaflet *leaflet; + GtkButton *leaflet_previous; + GtkListBox *topics; +}; + +G_DEFINE_TYPE(FlashcardsAppWindow, flashcards_app_window, ADW_TYPE_APPLICATION_WINDOW); + +static void +show_about (GtkWindow *win) +{ + adw_show_about_window (win, + "application-name", _("Flashcards"), + "application-icon", "org.gnome.Builder", + "developer-name", "Tobias Krause", + "version", "1.0", + "license-type", GTK_LICENSE_MIT_X11, + NULL); +} + +static void +on_category_selected(GtkListBox *box, GtkListBoxRow *row, gpointer user_data) +{ + if (row == NULL) + { + return; + } + printf("Test\n"); + FlashcardsAppWindow *win = FLASHCARDS_APP_WINDOW(gtk_widget_get_root(GTK_WIDGET(box))); + adw_leaflet_navigate(win->leaflet, ADW_NAVIGATION_DIRECTION_FORWARD); + + int id = gtk_list_box_row_get_index(gtk_list_box_get_selected_row(win->topics)); + printf("%d\n", id); + + show_about (GTK_WINDOW(gtk_widget_get_root (GTK_WIDGET (box)))); +} + +static void +on_navigate_back(GtkButton *button, gpointer user_data) +{ + printf("Test2\n"); + FlashcardsAppWindow *win = FLASHCARDS_APP_WINDOW(gtk_widget_get_root(GTK_WIDGET(button))); + gtk_list_box_unselect_all(win->topics); + adw_leaflet_navigate(win->leaflet, ADW_NAVIGATION_DIRECTION_BACK); +} + +static void +flashcards_app_window_init(FlashcardsAppWindow *win) +{ + gtk_widget_init_template(GTK_WIDGET(win)); + + database_connect(g_get_user_data_dir()); + database_create_tables(); + win->categories = database_load_categories(); + + GArray *categories = win->categories; + + for (int i = 0; i < categories->len; i++) + { + category c = g_array_index(categories, category, i); + printf("%d: %s\n", c.id, c.name); + + GtkWidget *child = adw_action_row_new(); + adw_preferences_row_set_title(ADW_PREFERENCES_ROW(child), c.name); + + gtk_list_box_append(GTK_LIST_BOX(win->topics), child); + } +} + +static void +flashcards_app_window_class_init(FlashcardsAppWindowClass *class) +{ + gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS(class), "/li/sopht/flashcards/window.ui"); + + gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), FlashcardsAppWindow, leaflet); + gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), FlashcardsAppWindow, leaflet_previous); + gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), FlashcardsAppWindow, topics); + + gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), on_navigate_back); + gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), on_category_selected); +} + +FlashcardsAppWindow * +flashcards_app_window_new(FlashcardsApp *app) +{ + return g_object_new(FLASHCARDS_APP_WINDOW_TYPE, "application", app, NULL); +} + +void flashcards_app_window_open(FlashcardsAppWindow *win, GFile *file) +{ +} diff --git a/src/flashcardsappwin.h b/src/flashcardsappwin.h new file mode 100644 index 0000000..88aff90 --- /dev/null +++ b/src/flashcardsappwin.h @@ -0,0 +1,15 @@ +#ifndef __FLASHCARDSAPPWIN_H +#define __FLASHCARDSAPPWIN_H + +#include +#include +#include "flashcardsapp.h" + +#define FLASHCARDS_APP_WINDOW_TYPE (flashcards_app_window_get_type()) +G_DECLARE_FINAL_TYPE(FlashcardsAppWindow, flashcards_app_window, FLASHCARDS, APP_WINDOW, AdwApplicationWindow) + +FlashcardsAppWindow *flashcards_app_window_new(FlashcardsApp *app); +void flashcards_app_window_open(FlashcardsAppWindow *win, + GFile *file); + +#endif /* __FLASHCARDSAPPWIN_H */ diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..7e9924b --- /dev/null +++ b/src/main.c @@ -0,0 +1,17 @@ +#define GETTEXT_PACKAGE "flashcards" + +#include +#include +#include + +#include "flashcardsapp.h" + +int +main (int argc, char *argv[]) +{ + bindtextdomain (GETTEXT_PACKAGE, "/usr/local/share/locale"); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + return g_application_run (G_APPLICATION (flashcards_app_new ()), argc, argv); +} diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 0000000..3d272df --- /dev/null +++ b/src/meson.build @@ -0,0 +1,5 @@ +sourcefiles = files('main.c', + 'flashcardsapp.c', + 'flashcardsappwin.c', + 'database.c' +) \ No newline at end of file