/* Eye on Manga - Manga collection software for Maemo 5
Copyright (C) 2010-2013 Tom Willemse
Eye on Manga is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
Eye on Manga is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with Eye on Manga. If not, see .
*/
#include "data.h"
#include
#include
#include
#include
#include
#include
#include
#include "eom.h"
static gboolean check_and_create_database(gchar*);
static gint create_new_database(const gchar*);
static gboolean execute_non_query(const char *sql,
int (*bind)(sqlite3_stmt*));
static sqlite3 *get_database(void);
static GList *get_manga_for_query(const gchar*);
static Manga *get_manga_from_statement(sqlite3_stmt*);
gboolean
data_add_manga(const gchar *name, gint total_qty)
{
gboolean result = FALSE;
char sql[] = "INSERT INTO manga (name, current_qty, total_qty) "
"VALUES (?, 0, ?)";
int bind_variables(sqlite3_stmt *stmt) {
return (sqlite3_bind_text(stmt, 1, name, -1, SQLITE_STATIC)
== SQLITE_OK)
&& (sqlite3_bind_int(stmt, 2, total_qty) == SQLITE_OK);
}
return execute_non_query(sql, bind_variables);
}
gboolean
data_add_to_manga(gint manga_id, gint count)
{
char sql[] = "UPDATE manga "
"SET current_qty = current_qty + ? "
"WHERE id = ?";
int bind_variables(sqlite3_stmt *stmt) {
return (sqlite3_bind_int(stmt, 1, count) == SQLITE_OK)
&& (sqlite3_bind_int(stmt, 2, manga_id) == SQLITE_OK);
}
return execute_non_query(sql, bind_variables);
}
gboolean
data_add_volume_to_manga(gint manga_id, gint volume)
{
char sql[] = "INSERT INTO volume "
"VALUES (?, ?, 0)";
int bind_variables(sqlite3_stmt *stmt) {
return (sqlite3_bind_int(stmt, 1, manga_id) == SQLITE_OK)
&& (sqlite3_bind_int(stmt, 2, volume) == SQLITE_OK);
}
return execute_non_query(sql, bind_variables);
}
gboolean
data_delete_manga(gint manga_id)
{
gboolean ret;
char *sql = "DELETE FROM volume "
"WHERE manga_id = ?";
int bind_variables(sqlite3_stmt *stmt) {
return sqlite3_bind_int(stmt, 1, manga_id) == SQLITE_OK;
}
ret = execute_non_query(sql, bind_variables);
if (ret) {
sql = "DELETE FROM manga "
"WHERE id = ?";
ret = execute_non_query(sql, bind_variables);
}
return ret;
}
GList *
data_get_incomplete_manga(void)
{
const char *sql =
" SELECT id, "
" name, "
" current_qty, "
" total_qty "
" FROM manga "
" WHERE current_qty != total_qty "
" ORDER BY name "
" COLLATE NOCASE ";
return get_manga_for_query(sql);
}
GList *
data_get_manga(void)
{
const char *sql =
" SELECT id, "
" name, "
" current_qty, "
" total_qty "
" FROM manga "
" ORDER BY name "
" COLLATE NOCASE ";
return get_manga_for_query(sql);
}
Manga *
data_get_manga_by_id(gint manga_id)
{
sqlite3 *db = get_database();
sqlite3_stmt *stmt;
Manga *manga = NULL;
if (db) {
int res;
char *sql =
"SELECT id, name, current_qty, total_qty "
"FROM manga "
"WHERE id = ?";
res = sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, NULL);
if (res == SQLITE_OK
&& (sqlite3_bind_int(stmt, 1, manga_id) == SQLITE_OK)
&& sqlite3_step(stmt) == SQLITE_ROW)
manga = get_manga_from_statement(stmt);
sqlite3_finalize(stmt);
sqlite3_close(db);
}
return manga;
}
GList *
data_get_unread_manga(void)
{
const char *sql =
" SELECT m.id, "
" m.name, "
" m.current_qty, "
" m.total_qty "
" FROM manga m "
" JOIN volume ON (m.id = manga_id) "
" WHERE read = 0 "
" GROUP BY m.id "
" ORDER BY name "
" COLLATE NOCASE ";
return get_manga_for_query(sql);
}
void
data_get_volumes_for_manga(Manga *manga)
{
gint count;
Volume *volumes = NULL;
sqlite3 *db = get_database();
sqlite3_stmt *stmt;
count = 0;
if (db) {
int res;
char *sql =
"SELECT COUNT(id) "
"FROM volume "
"WHERE manga_id = ?";
res = sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, NULL);
if (res == SQLITE_OK
&& (sqlite3_bind_int(stmt, 1, manga->id) == SQLITE_OK)
&& sqlite3_step(stmt) == SQLITE_ROW)
count = sqlite3_column_int(stmt, 0);
sqlite3_finalize(stmt);
volumes = calloc(sizeof(Volume), count);
if (count > 0) {
sql =
"SELECT id, read "
"FROM volume "
"WHERE manga_id = ?";
res = sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, NULL);
if (res == SQLITE_OK
&& (sqlite3_bind_int(stmt, 1, manga->id) == SQLITE_OK)) {
gint i = 0;
while (sqlite3_step(stmt) == SQLITE_ROW) {
volumes[i].number = sqlite3_column_int(stmt, 0);
volumes[i].read = sqlite3_column_int(stmt, 1);
i++;
};
}
sqlite3_finalize(stmt);
}
sqlite3_close(db);
}
manga->vol_count = count;
manga->volumes = volumes;
}
gboolean
data_mark_volume_read(int read, gint manga_id, gint volume)
{
char sql[] = "UPDATE volume "
"SET read = ? "
"WHERE manga_id = ? "
"AND id = ?";
int bind_variables(sqlite3_stmt *stmt) {
return (sqlite3_bind_int(stmt, 1, read) == SQLITE_OK)
&& (sqlite3_bind_int(stmt, 2, manga_id) == SQLITE_OK)
&& (sqlite3_bind_int(stmt, 3, volume) == SQLITE_OK);
}
return execute_non_query(sql, bind_variables);
}
gboolean
data_remove_volume_from_manga(gint manga_id, gint volume)
{
char sql[] = "DELETE FROM volume "
"WHERE manga_id = ? "
"AND id = ?";
int bind_variables(sqlite3_stmt *stmt) {
return (sqlite3_bind_int(stmt, 1, manga_id) == SQLITE_OK)
&& (sqlite3_bind_int(stmt, 2, volume) == SQLITE_OK);
}
return execute_non_query(sql, bind_variables);
}
gboolean
data_update_manga(gint manga_id, const gchar *name, gint total_qty)
{
gboolean ret;
char *sql = "UPDATE manga "
"SET name = ?001, total_qty = ?002, "
" current_qty = MIN(current_qty, ?002) "
"WHERE id = ?003";
int bind_variables(sqlite3_stmt *stmt) {
return (sqlite3_bind_text(stmt, 1, name, -1, SQLITE_STATIC) == SQLITE_OK)
&& (sqlite3_bind_int(stmt, 2, total_qty) == SQLITE_OK)
&& (sqlite3_bind_int(stmt, 3, manga_id) == SQLITE_OK);
}
ret = execute_non_query(sql, bind_variables);
if (ret) {
sql = "DELETE FROM volume "
"WHERE manga_id = ?"
"AND id > ?";
int bind_variables(sqlite3_stmt *stmt) {
return (sqlite3_bind_int(stmt, 1, manga_id) == SQLITE_OK)
&& (sqlite3_bind_int(stmt, 2, total_qty) == SQLITE_OK);
}
ret = execute_non_query(sql, bind_variables);
}
return ret;
}
static gboolean
check_and_create_database(gchar *data_file)
{
if (!access(data_file, R_OK) == 0)
if (create_new_database(data_file)) {
g_printerr("Couldn't create a new database\n");
return FALSE; /* Couldn't create database, can't continue */
}
return TRUE;
}
static gint
create_new_database(const gchar *filename)
{
sqlite3 *db;
char *zErrMsg = 0;
int rc;
if (access(eom_get_config_dir(), R_OK) == -1) {
if (mkdir(eom_get_config_dir(), S_IRWXU) == -1)
g_printerr("Can't create directory: %s\n", strerror(errno));
}
/* Open database connection, create file */
rc = sqlite3_open(filename, &db);
if (rc) {
g_printerr("Can't open database: %s\n", sqlite3_errmsg(db));
if (db)
sqlite3_close(db);
return -1;
}
/* Create collections table */
rc = sqlite3_exec(db,
"CREATE TABLE manga("
"id INTEGER PRIMARY KEY,"
"name VARCHAR(100),"
"current_qty INTEGER,"
"total_qty INTEGER"
")", NULL, NULL, &zErrMsg);
if (rc != SQLITE_OK) {
g_printerr("Can't create manga table: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
return -1;
}
/* Create items table */
rc = sqlite3_exec(db,
" CREATE TABLE volume( "
" manga_id INTEGER, "
" id INTEGER, "
" read INTEGER, "
" PRIMARY KEY(manga_id, id), "
" FOREIGN KEY(manga_id) "
" REFERENCES manga(id)) ",
NULL, NULL, &zErrMsg);
if (rc != SQLITE_OK) {
g_printerr("Can't create volume table: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
return -1;
}
sqlite3_close(db);
return 0;
}
static gboolean
execute_non_query(const gchar *sql, int (*bind)(sqlite3_stmt *))
{
sqlite3 *db = get_database();
sqlite3_stmt *stmt;
gboolean result;
result = FALSE;
if (db) {
int res =
sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, NULL);
result = (res == SQLITE_OK
&& (!bind || bind(stmt))
&& sqlite3_step(stmt) == SQLITE_DONE);
sqlite3_finalize(stmt);
sqlite3_close(db);
}
return result;
}
static sqlite3 *
get_database(void)
{
sqlite3 *db;
gchar *data_file = eom_get_data_file();
if (check_and_create_database(data_file)) {
if (sqlite3_open(data_file, &db) == SQLITE_OK)
return db;
else
sqlite3_close(db);
}
return NULL;
}
static GList *
get_manga_for_query(const gchar *query)
{
sqlite3 *database = get_database();
sqlite3_stmt *statement;
GList *list = NULL;
if (database) {
int res = sqlite3_prepare_v2(database, query, strlen(query),
&statement, NULL);
if (res == SQLITE_OK) {
while (sqlite3_step(statement) == SQLITE_ROW) {
Manga *manga = get_manga_from_statement(statement);
list = g_list_append(list, (gpointer)manga);
}
}
else
g_print("error %d: %s\n", res, sqlite3_errmsg(database));
/* Release the compiled statement from memory */
sqlite3_finalize(statement);
sqlite3_close(database);
}
return list;
}
static Manga *
get_manga_from_statement(sqlite3_stmt *stmt)
{
Manga *manga = (Manga *)malloc(sizeof(Manga));
manga->id = sqlite3_column_int(stmt, 0);
manga->name = g_strdup(sqlite3_column_text(stmt, 1));
manga->current_qty = sqlite3_column_int(stmt, 2);
manga->total_qty = sqlite3_column_int(stmt, 3);
manga->volumes = NULL;
manga->vol_count = 0;
return manga;
}