SRC = main.c
SRC += application.c
SRC += window.c
+SRC += types.c
SRC += store.c
+SRC += store_sqlite.c
OBJ = $(SRC:%.c=../build/application/%.$(OBJ_EXT))
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
- * Copyright 2024 Olaf Wintermann. All rights reserved.
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
#include "window.h"
#include "store.h"
+#include <unistd.h>
+#include <limits.h>
+
+#include <cx/mempool.h>
+
void application_startup(UiEvent *event, void *data) {
- if(init_note_store) {
+ if(init_note_store()) {
+ fprintf(stderr, "note store initialization failed\n");
return;
}
+ // Get environment required by settings
+ char hostname[HOST_NAME_MAX];
+ char *host = NULL;
+ if(gethostname(hostname, sizeof(hostname)) == 0) {
+ host = hostname;
+ }
+ char *user = getenv("USER");
+
+ // Get available user settings
+ // The first entry should be the default, however it must be checked if
+ // it is valid for this environment
+ CxMempool *mp = cxMempoolCreate(64, NULL);
+ CxList *usersettings = note_store_get_user_settings(mp->allocator, host, user, NULL);
+ int settings_valid = 0;
+ if(usersettings && cxListSize(usersettings) > 0) {
+ UserSettings *settings = cxListAt(usersettings, 0);
+ if(user_settings_is_valid(settings, host, user, NULL)) {
+ note_store_set_settings(
+ settings->host,
+ settings->user,
+ settings->profile_name,
+ settings->default_repository_id,
+ settings->default_collection_id);
+ settings_valid = 1;
+ }
+ }
+
+ if(!settings_valid) {
+ // no valid settings (or no settings)
+ // TODO: Show a initialization dialog, where the user could
+ // select available settings or create new settings
+ fprintf(stderr, "TODO: settings prompt\n");
+ exit(-1);
+ }
+
+ cxMempoolFree(mp);
+
window_create();
}
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
- * Copyright 2024 Olaf Wintermann. All rights reserved.
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
#include <ui/ui.h>\r
\r
#include "application.h"\r
+#include "types.h"\r
\r
int app_main(int argc, char **argv) {\r
+ register_types();\r
ui_init("note", argc, argv);\r
ui_onstartup(application_startup, NULL);\r
ui_main();\r
#include <errno.h>
#include <sys/stat.h>
+#include "types.h"
+#include "store_sqlite.h"
+
#include "../dbutils/dbutils/db.h"
#include "../dbutils/dbutils/sqlite.h"
static DBUConnection *connection;
+static char *settings_host;
+static char *settings_user;
+static char *settings_profile_name;
+static int64_t settings_default_repository_id;
+static int64_t settings_default_node_id;
+
+/*
+ * Creates a connection to the note database and initializes tables if necessary
+ *
+ * Returns:
+ * 0 on success
+ * 1 on error
+ */
int init_note_store() {
char *sqlite_db_file = ui_configfile(NOTES_DB_FILE);
return 1;
}
+ if(init_db) {
+ if(store_sqlite_init_db(connection)) {
+ return 1;
+ }
+ }
+
return 0;
}
+
+/*
+ * Gets a list of all available user settings, sorted by relevance. Usually
+ * the first item should be used as default settings, however the
+ * host/user/profile fields should be checked.
+ */
+CxList* note_store_get_user_settings(const CxAllocator *a, const char *host, const char *user, const char *profile) {
+ DBUQuery *query = connection->createQuery(connection, NULL);
+ dbuQuerySetSQL(query, "select * from user_settings;");
+ if(host) {
+ dbuQuerySetParamString(query, 0, cx_str(host));
+ } else {
+ dbuQuerySetParamNull(query, 0);
+ }
+ if(user) {
+ dbuQuerySetParamString(query, 1, cx_str(user));
+ } else {
+ dbuQuerySetParamNull(query, 1);
+ }
+ if(profile) {
+ dbuQuerySetParamString(query, 2, cx_str(profile));
+ } else {
+ dbuQuerySetParamNull(query, 2);
+ }
+
+ DBUObjectBuilder *builder = dbuObjectBuilder(usersettings_class, query, a);
+ CxList *usersettings = dbuObjectBuilderGetList(builder);
+ dbuObjectBuilderDestroy(builder);
+
+ return usersettings;
+}
+
+void close_note_store() {
+ connection->free(connection);
+}
+
+
+/*
+ * Checks if the settings object complies to the specified host/user/profile.
+ *
+ * A settings field (like host) must be NULL or have the same specified value.
+ *
+ * Returns:
+ * 1: settings are valid
+ * 0: settings are not valid
+ */
+int user_settings_is_valid(
+ UserSettings *settings,
+ const char *host,
+ const char *user,
+ const char *profile)
+{
+ if(!settings) return 0;
+ if(settings->host && host && strcmp(settings->host, host)) {
+ return 0;
+ }
+ if(settings->user && host && strcmp(settings->user, user)) {
+ return 0;
+ }
+ if(settings->profile_name && profile && strcmp(settings->profile_name, profile)) {
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Sets global settings
+ */
+void note_store_set_settings(
+ const char *host,
+ const char *user,
+ const char *profile_name,
+ int64_t default_repository_id,
+ int64_t default_node_id)
+{
+ settings_host = host ? strdup(host) : NULL;
+ settings_user = user ? strdup(user) : NULL;
+ settings_profile_name = profile_name ? strdup(profile_name) : NULL;
+ settings_default_repository_id = default_repository_id;
+ settings_default_node_id = default_node_id;
+}
+
#define STORE_H
#include "application.h"
+#include "types.h"
+#include <cx/list.h>
#ifdef __cplusplus
extern "C" {
#endif
#define NOTES_DB_FILE "notes.db"
-
+
int init_note_store();
+CxList* note_store_get_user_settings(const CxAllocator *a, const char *host, const char *user, const char *profile);
+
+void close_note_store();
+
+int user_settings_is_valid(
+ UserSettings *settings,
+ const char *host,
+ const char *user,
+ const char *profile);
+
+void note_store_set_settings(
+ const char *host,
+ const char *user,
+ const char *profile_name,
+ int64_t default_repository_id,
+ int64_t default_node_id);
+
#ifdef __cplusplus
}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "store_sqlite.h"
+
+#include <stdio.h>
+#include "../dbutils/dbutils/sqlite.h"
+
+
+#define SQL_CREATE_TABLE_NOTEDB "create table note_db( " \
+ "version int, " \
+ "created text);"
+#define SQL_CREATE_TABLE_REPOSITORIES "create table repositories( " \
+ "repository_id integer primary key, " \
+ "name text not null unique, " \
+ "url text, " \
+ "encryption integer, " \
+ "default_key text, " \
+ "authmethod integer, " \
+ "local_path text);"
+#define SQL_CREATE_TABLE_COLLECTIONS "create table collections( " \
+ "collection_id integer primary key, " \
+ "parent_id integer, " \
+ "repository_id integer, " \
+ "name text not null, " \
+ "display_name text, " \
+ "type text, " \
+ "created_by text, " \
+ "created_at text, " \
+ "foreign key (parent_id) references collections(collection_id), " \
+ "foreign key (repository_id) references repositories(repository_id)" \
+ ");"
+#define SQL_CREATE_TABLE_USER_SETTINGS "create table user_settings( " \
+ "host text, " \
+ "user text, " \
+ "profile_name text, " \
+ "default_repository_id integer, " \
+ "default_collection_id integer, " \
+ "created_by text, " \
+ "created_at text, " \
+ "foreign key (default_repository_id) references repositories(repository_id), " \
+ "foreign key (default_collection_id) references collections(collection_id), " \
+ "unique (host, user, profile_name)" \
+ ");"
+
+int store_sqlite_init_db(DBUConnection *connection) {
+ char *sql[] = {
+ SQL_CREATE_TABLE_NOTEDB,
+ SQL_CREATE_TABLE_REPOSITORIES,
+ SQL_CREATE_TABLE_COLLECTIONS,
+ SQL_CREATE_TABLE_USER_SETTINGS
+ };
+ int nsql = sizeof(sql) / sizeof(char*);
+
+ for(int i=0;i<nsql;i++) {
+ if(dbuConnectionExec(connection, sql[i])) {
+ fprintf(stderr, "sqlite error: query failed\n%s\n", sql[i]);
+ return 1;
+ }
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef STORE_SQLITE_H
+#define STORE_SQLITE_H
+
+#include "../dbutils/dbutils/db.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int store_sqlite_init_db(DBUConnection *connection);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* STORE_SQLITE_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "types.h"
+
+static DBUContext *ctx;
+
+DBUClass *usersettings_class;
+DBUClass *repository_class;
+DBUClass *collection_class;
+
+DBUContext* get_dbu_context() {
+ return ctx;
+}
+
+void register_types() {
+ ctx = dbuContextCreate();
+
+ usersettings_class = dbuRegisterClassWithoutPK(ctx, "user_settings", sizeof(UserSettings));
+ dbuClassAdd(usersettings_class, UserSettings, host);
+ dbuClassAdd(usersettings_class, UserSettings, user);
+ dbuClassAdd(usersettings_class, UserSettings, profile_name);
+ dbuClassAdd(usersettings_class, UserSettings, default_repository_id);
+ dbuClassAdd(usersettings_class, UserSettings, default_collection_id);
+ dbuClassAdd(usersettings_class, UserSettings, created_by);
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2025 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef APP_TYPES_H
+#define APP_TYPES_H
+
+#include "../dbutils/dbutils/dbutils.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct UserSettings UserSettings;
+typedef struct Repository Repository;
+typedef struct Collection Collection;
+
+struct UserSettings {
+ char *host;
+ char *user;
+ char *profile_name;
+ int64_t default_repository_id;
+ int64_t default_collection_id;
+ char *created_by;
+ // created_at
+};
+
+struct Repository {
+ int64_t repository_id;
+ char *name;
+ char *url;
+ char *local_path;
+ char *default_key;
+ int authmethod;
+ bool encryption;
+};
+
+struct Collection {
+ int64_t collection_id;
+ int64_t parent_id;
+ int64_t repository_id;
+ char *name;
+ char *display_name;
+ char *type;
+};
+
+extern DBUClass *usersettings_class;
+extern DBUClass *repository_class;
+extern DBUClass *collection_class;
+
+void register_types();
+
+DBUContext* get_dbu_context();
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* APP_TYPES_H */
+
// splitpane right: content
ui_grid(obj, .columnspacing = 10, .rowspacing = 10) {
- ui_label(obj, .label = "Title");
+ ui_label(obj, .label = "Title", .vfill = TRUE);
ui_textfield(obj, .varname = "note_title", .hexpand = TRUE, .groups = UI_GROUPS(APP_STATE_NOTE_SELECTED));
ui_newline(obj);
#include "db.h"
#include "dbutils/db.h"
+int dbuConnectionExec(DBUConnection *conn, const char *sql) {
+ DBUQuery *q = conn->createQuery(conn, NULL);
+ int err = 1;
+ if(!dbuQuerySetSQL(q, sql)) {
+ err = dbuQueryExec(q);
+ }
+ dbuQueryFree(q);
+ return err;
+}
+
+
+
int dbuQuerySetSQL(DBUQuery *q, const char *sql) {
return q->setSQL(q, sql);
}
cxMapPut(context->classes, name, cls);
return cls;
}
+
+DBUClass* dbuRegisterClassWithoutPK(
+ DBUContext *context,
+ const char *name,
+ size_t obj_size)
+{
+ DBUClass *cls = dbuClassCreate(name);
+ cls->context = context;
+ cls->obj_size = obj_size;
+ cxMapPut(context->classes, name, cls);
+ return cls;
+}
int rowIndex;
};
+int dbuConnectionExec(DBUConnection *conn, const char *sql);
int dbuQuerySetSQL(DBUQuery *q, const char *sql);
int dbuQuerySetParamString(DBUQuery *q, int index, cxstring str);
size_t obj_size,
const char *primary_key_column,
off_t primary_key_offset);
+DBUClass* dbuRegisterClassWithoutPK(
+ DBUContext *context,
+ const char *name,
+ size_t obj_size);
void dbuClassSetPrimaryKeyInt32(DBUClass *cls, const char *column_name, off_t offset);
void dbuClassSetPrimaryKeyUInt32(DBUClass *cls, const char *column_name, off_t offset);
DBUField *type_field = field.field;
if(type_field && current_obj) {
if(isnull) {
- field.field->initDefaultValue(field.field, a, current_obj);
+ if(field.field->initDefaultValue) {
+ field.field->initDefaultValue(field.field, a, current_obj);
+ }
} else {
cxstring text = result->getText(result, i);
//printf("getText(%d) = %s\n", i, text.ptr);