Merge pull request #43 from gergelypolonkai/chart-db

Save charts in a database instead of separate files
This commit is contained in:
Gergely Polonkai 2014-08-03 11:22:14 +02:00
commit 82c8b193a1
16 changed files with 1923 additions and 131 deletions

3
.gitmodules vendored
View File

@ -0,0 +1,3 @@
[submodule "libgd"]
path = libgd
url = git://git.gnome.org/libgd.git

View File

@ -1,5 +1,5 @@
ACLOCAL_AMFLAGS = -I m4 ACLOCAL_AMFLAGS = -I m4 -I libgd ${ACLOCAL_FLAGS}
SUBDIRS = src po data help SUBDIRS = libgd src po data help
EXTRA_DIST = config.rpath ChangeLog EXTRA_DIST = config.rpath ChangeLog

View File

@ -4,6 +4,8 @@
srcdir=`dirname $0` srcdir=`dirname $0`
test -z "$srcdir" && srcdir=. test -z "$srcdir" && srcdir=.
ACLOCAL_FLAGS="-I libgd $ACLOCAL_FLAGS"
PKG_NAME="astrognome" PKG_NAME="astrognome"
(test -f $srcdir/configure.ac \ (test -f $srcdir/configure.ac \
@ -18,5 +20,7 @@ which gnome-autogen.sh || {
exit 1 exit 1
} }
git submodule update --init --recursive
REQUIRED_AUTOMAKE_VERSION=1.9 . gnome-autogen.sh REQUIRED_AUTOMAKE_VERSION=1.9 . gnome-autogen.sh

View File

@ -35,10 +35,16 @@ PKG_CHECK_MODULES([GTK], [gtk+-3.0 >= 3.8])
PKG_CHECK_MODULES([LIBXML], [libxml-2.0]) PKG_CHECK_MODULES([LIBXML], [libxml-2.0])
PKG_CHECK_MODULES([LIBXSLT], [libexslt]) PKG_CHECK_MODULES([LIBXSLT], [libexslt])
PKG_CHECK_MODULES([WEBKIT], [webkit2gtk-3.0]) PKG_CHECK_MODULES([WEBKIT], [webkit2gtk-3.0])
PKG_CHECK_MODULES([GDA], [libgda-5.0 libgda-sqlite-5.0])
PKG_CHECK_MODULES([SWE_GLIB], [swe-glib >= 2.1.0]) PKG_CHECK_MODULES([SWE_GLIB], [swe-glib >= 2.1.0])
LIBGD_INIT([
main-view
])
AC_CONFIG_FILES([ AC_CONFIG_FILES([
Makefile Makefile
libgd/Makefile
src/Makefile src/Makefile
help/Makefile help/Makefile
po/Makefile.in po/Makefile.in

1
libgd Submodule

@ -0,0 +1 @@
Subproject commit 62f9b8b92599b38d986bd26d5780edd400d318c9

View File

@ -18,6 +18,7 @@ astrognome_source_files = \
ag-chart.c \ ag-chart.c \
ag-settings.c \ ag-settings.c \
ag-preferences.c \ ag-preferences.c \
ag-db.c \
astrognome.c \ astrognome.c \
$(NULL) $(NULL)
@ -26,11 +27,11 @@ EXTRA_DIST = \
ag.gresource.xml \ ag.gresource.xml \
$(NULL) $(NULL)
AM_CPPFLAGS = -DG_LOG_DOMAIN=\"Astrognome\" -DLOCALEDIR=\"$(localedir)\" -DPKGDATADIR=\"$(pkgdatadir)\" AM_CPPFLAGS = -DG_LOG_DOMAIN=\"Astrognome\" -DLOCALEDIR=\"$(localedir)\" -DPKGDATADIR=\"$(pkgdatadir)\" -I$(top_srcdir)/libgd
bin_PROGRAMS = astrognome bin_PROGRAMS = astrognome
astrognome_SOURCES = $(astrognome_source_files) $(BUILT_SOURCES) astrognome_SOURCES = $(astrognome_source_files) $(BUILT_SOURCES)
astrognome_LDADD = $(SWE_GLIB_LIBS) $(GTK_LIBS) $(LIBXML_LIBS) $(LIBXSLT_LIBS) $(WEBKIT_LIBS) astrognome_LDADD = $(SWE_GLIB_LIBS) $(GTK_LIBS) $(LIBXML_LIBS) $(LIBXSLT_LIBS) $(WEBKIT_LIBS) $(GDA_LIBS) $(top_builddir)/libgd/libgd.la
astrognome_LDFLAGS = -rdynamic astrognome_LDFLAGS = -rdynamic
astrognome_CFLAGS = $(SWE_GLIB_CFLAGS) $(CFLAGS) $(GTK_CFLAGS) $(LIBXML_CFLAGS) $(LIBXSLT_CFLAGS) $(WEBKIT_CFLAGS) -Wall astrognome_CFLAGS = $(SWE_GLIB_CFLAGS) $(CFLAGS) $(GTK_CFLAGS) $(LIBXML_CFLAGS) $(LIBXSLT_CFLAGS) $(WEBKIT_CFLAGS) $(GDA_CFLAGS) -Wall

View File

@ -68,7 +68,10 @@ ag_app_create_window(AgApp *app)
static void static void
new_window_cb(GSimpleAction *action, GVariant *parameter, gpointer user_data) new_window_cb(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{ {
ag_app_create_window(AG_APP(user_data)); AgWindow *window = AG_WINDOW(ag_app_create_window(AG_APP(user_data)));
ag_window_load_chart_list(window);
ag_window_change_tab(window, "list");
} }
static void static void
@ -130,12 +133,11 @@ quit_cb(GSimpleAction *action, GVariant *parameter, gpointer user_data)
} }
static void static void
ag_app_open_chart(AgApp *app, GFile *file) ag_app_import_chart(AgApp *app, GFile *file)
{ {
GtkWidget *window; GtkWidget *window;
AgChart *chart; AgChart *chart;
GError *err = NULL; GError *err = NULL;
gchar *uri;
if ((chart = ag_chart_load_from_file(file, &err)) == NULL) { if ((chart = ag_chart_load_from_file(file, &err)) == NULL) {
g_print("Error: '%s'\n", err->message); g_print("Error: '%s'\n", err->message);
@ -146,14 +148,12 @@ ag_app_open_chart(AgApp *app, GFile *file)
window = ag_app_create_window(app); window = ag_app_create_window(app);
ag_window_set_chart(AG_WINDOW(window), chart); ag_window_set_chart(AG_WINDOW(window), chart);
ag_window_update_from_chart(AG_WINDOW(window)); ag_window_update_from_chart(AG_WINDOW(window));
uri = g_file_get_uri(file); g_action_group_activate_action(G_ACTION_GROUP(window), "save", NULL);
ag_window_set_uri(AG_WINDOW(window), uri);
g_free(uri);
ag_window_change_tab(AG_WINDOW(window), "chart"); ag_window_change_tab(AG_WINDOW(window), "chart");
} }
static void static void
open_cb(GSimpleAction *action, GVariant *parameter, gpointer user_data) ag_app_import_cb(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{ {
gint response; gint response;
GtkWidget *fs; GtkWidget *fs;
@ -163,7 +163,7 @@ open_cb(GSimpleAction *action, GVariant *parameter, gpointer user_data)
NULL, NULL,
GTK_FILE_CHOOSER_ACTION_OPEN, GTK_FILE_CHOOSER_ACTION_OPEN,
_("_Cancel"), GTK_RESPONSE_CANCEL, _("_Cancel"), GTK_RESPONSE_CANCEL,
_("_Open"), GTK_RESPONSE_ACCEPT, _("_Import"), GTK_RESPONSE_ACCEPT,
NULL); NULL);
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(fs), filter_all); gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(fs), filter_all);
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(fs), filter_chart); gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(fs), filter_chart);
@ -190,7 +190,7 @@ open_cb(GSimpleAction *action, GVariant *parameter, gpointer user_data)
} }
file = g_file_new_for_commandline_arg(data); file = g_file_new_for_commandline_arg(data);
ag_app_open_chart(AG_APP(user_data), file); ag_app_import_chart(AG_APP(user_data), file);
} }
} }
@ -250,7 +250,7 @@ static GActionEntry app_entries[] = {
{ "about", about_cb, NULL, NULL, NULL }, { "about", about_cb, NULL, NULL, NULL },
{ "quit", quit_cb, NULL, NULL, NULL }, { "quit", quit_cb, NULL, NULL, NULL },
{ "raise", raise_cb, NULL, NULL, NULL }, { "raise", raise_cb, NULL, NULL, NULL },
{ "open", open_cb, NULL, NULL, NULL }, { "import", ag_app_import_cb, NULL, NULL, NULL },
{ "help", help_cb, NULL, NULL, NULL }, { "help", help_cb, NULL, NULL, NULL },
}; };
@ -348,12 +348,15 @@ startup(GApplication *gapp)
} }
static void static void
ag_app_open(GApplication *gapp, GFile **files, gint n_files, const gchar *hint) ag_app_import(GApplication *gapp,
GFile **files,
gint n_files,
const gchar *hint)
{ {
gint i; gint i;
for (i = 0; i < n_files; i++) { for (i = 0; i < n_files; i++) {
ag_app_open_chart(AG_APP(gapp), files[i]); ag_app_import_chart(AG_APP(gapp), files[i]);
} }
} }
@ -441,7 +444,49 @@ ag_app_class_init(AgAppClass *klass)
GApplicationClass *application_class = G_APPLICATION_CLASS(klass); GApplicationClass *application_class = G_APPLICATION_CLASS(klass);
application_class->startup = startup; application_class->startup = startup;
application_class->open = ag_app_open; application_class->open = ag_app_import;
}
gint
ag_app_buttoned_dialog(GtkWidget *window,
GtkMessageType message_type,
const gchar *message,
const gchar *first_button_text,
...)
{
va_list ap;
const gchar *button_text;
gint response_id;
GtkWidget *dialog;
dialog = gtk_message_dialog_new(
GTK_WINDOW(window),
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
message_type,
GTK_BUTTONS_NONE,
"%s",
message
);
if (first_button_text) {
button_text = first_button_text;
va_start(ap, first_button_text);
response_id = va_arg(ap, gint);
gtk_dialog_add_button(GTK_DIALOG(dialog), button_text, response_id);
while ((button_text = va_arg(ap, gchar *)) != NULL) {
response_id = va_arg(ap, gint);
gtk_dialog_add_button(GTK_DIALOG(dialog), button_text, response_id);
}
va_end(ap);
}
response_id = gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
return response_id;
} }
void void
@ -451,21 +496,18 @@ ag_app_message_dialog(GtkWidget *window,
{ {
gchar *msg; gchar *msg;
va_list args; va_list args;
GtkWidget *dialog;
va_start(args, fmt); va_start(args, fmt);
msg = g_strdup_vprintf(fmt, args); msg = g_strdup_vprintf(fmt, args);
va_end(args); va_end(args);
dialog = gtk_message_dialog_new( ag_app_buttoned_dialog(
GTK_WINDOW(window), window,
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
message_type, message_type,
GTK_BUTTONS_OK, msg,
"%s", _("Close"), GTK_RESPONSE_CLOSE,
msg NULL
); );
g_free(msg); g_free(msg);
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
} }

View File

@ -47,6 +47,11 @@ void ag_app_run_action(AgApp *app,
gboolean is_remote, gboolean is_remote,
const AstrognomeOptions *options); const AstrognomeOptions *options);
gint ag_app_buttoned_dialog(GtkWidget *window,
GtkMessageType message_type,
const gchar *message,
const gchar *first_button_text, ...);
void ag_app_message_dialog(GtkWidget *window, void ag_app_message_dialog(GtkWidget *window,
GtkMessageType message_type, GtkMessageType message_type,
gchar *fmt, ...); gchar *fmt, ...);

View File

@ -10,6 +10,7 @@
#include <locale.h> #include <locale.h>
#include <math.h> #include <math.h>
#include "ag-db.h"
#include "ag-chart.h" #include "ag-chart.h"
typedef struct _AgChartPrivate { typedef struct _AgChartPrivate {
@ -921,6 +922,70 @@ ag_chart_load_from_file(GFile *file, GError **err)
return chart; return chart;
} }
AgChart *
ag_chart_new_from_db_save(AgDbSave *save_data, GError **err)
{
GsweTimestamp *timestamp;
gchar *house_system_enum_name;
GTypeClass *house_system_class;
GEnumValue *enum_value;
GsweHouseSystem house_system;
AgChart *chart;
if (save_data == NULL) {
g_set_error(
err,
AG_CHART_ERROR, AG_CHART_ERROR_EMPTY_RECORD,
"Invalid chart"
);
return NULL;
}
house_system_enum_name = g_utf8_strdown(save_data->house_system, -1);
house_system_class = g_type_class_ref(GSWE_TYPE_HOUSE_SYSTEM);
if ((enum_value = g_enum_get_value_by_nick(
G_ENUM_CLASS(house_system_class),
house_system_enum_name
)) == NULL) {
g_free(house_system_enum_name);
g_set_error(
err,
AG_CHART_ERROR, AG_CHART_ERROR_INVALID_HOUSE_SYSTEM,
"Invalid house system: '%s'",
save_data->house_system
);
return NULL;
}
g_free(house_system_enum_name);
house_system = enum_value->value;
timestamp = gswe_timestamp_new_from_gregorian_full(
save_data->year, save_data->month, save_data->day,
save_data->hour, save_data->minute, save_data->second, 0,
save_data->timezone
);
chart = ag_chart_new_full(
timestamp,
save_data->longitude,
save_data->latitude,
save_data->altitude,
house_system
);
ag_chart_set_name(chart, save_data->name);
ag_chart_set_country(chart, save_data->country);
ag_chart_set_city(chart, save_data->city);
ag_chart_set_note(chart, save_data->note);
return chart;
}
static xmlDocPtr static xmlDocPtr
create_save_doc(AgChart *chart) create_save_doc(AgChart *chart)
{ {
@ -1475,3 +1540,56 @@ const gchar *ag_chart_get_note(AgChart *chart)
return priv->note; return priv->note;
} }
AgDbSave *
ag_chart_get_db_save(AgChart *chart, gint db_id)
{
GsweCoordinates *coords;
AgChartPrivate *priv = ag_chart_get_instance_private(chart);
AgDbSave *save_data = g_new0(AgDbSave, 1);
GsweTimestamp *timestamp = gswe_moment_get_timestamp(GSWE_MOMENT(chart));
GEnumClass *house_system_class;
GEnumValue *house_system_enum;
save_data->db_id = db_id;
save_data->name = g_strdup(priv->name);
save_data->country = g_strdup(priv->country);
save_data->city = g_strdup(priv->city);
coords = gswe_moment_get_coordinates(GSWE_MOMENT(chart));
save_data->longitude = coords->longitude;
save_data->latitude = coords->latitude;
save_data->altitude = coords->altitude;
g_free(coords);
save_data->year = gswe_timestamp_get_gregorian_year(
timestamp,
NULL
);
save_data->month = gswe_timestamp_get_gregorian_month(
timestamp,
NULL
);
save_data->day = gswe_timestamp_get_gregorian_day(timestamp, NULL);
save_data->hour = gswe_timestamp_get_gregorian_hour(
timestamp,
NULL
);
save_data->minute = gswe_timestamp_get_gregorian_minute(
timestamp,
NULL
);
save_data->second = gswe_timestamp_get_gregorian_second(
timestamp,
NULL
);
save_data->timezone = gswe_timestamp_get_gregorian_timezone(timestamp);
house_system_class = g_type_class_ref(GSWE_TYPE_HOUSE_SYSTEM);
house_system_enum = g_enum_get_value(
house_system_class,
gswe_moment_get_house_system(GSWE_MOMENT(chart))
);
save_data->house_system = g_strdup(house_system_enum->value_nick);
g_type_class_unref(house_system_class);
save_data->note = g_strdup(priv->note);
return save_data;
}

View File

@ -2,13 +2,18 @@
#define __AG_CHART_H__ #define __AG_CHART_H__
#include <glib-object.h> #include <glib-object.h>
#include <gtk/gtk.h>
#include <swe-glib.h> #include <swe-glib.h>
#include "ag-db.h"
G_BEGIN_DECLS G_BEGIN_DECLS
typedef enum { typedef enum {
AG_CHART_ERROR_LIBXML, AG_CHART_ERROR_LIBXML,
AG_CHART_ERROR_CORRUPT_FILE, AG_CHART_ERROR_CORRUPT_FILE,
AG_CHART_ERROR_EMPTY_RECORD,
AG_CHART_ERROR_INVALID_HOUSE_SYSTEM,
} AgChartError; } AgChartError;
#define AG_TYPE_CHART (ag_chart_get_type()) #define AG_TYPE_CHART (ag_chart_get_type())
@ -46,6 +51,8 @@ AgChart *ag_chart_new_full(GsweTimestamp *timestamp,
AgChart *ag_chart_load_from_file(GFile *file, AgChart *ag_chart_load_from_file(GFile *file,
GError **err); GError **err);
AgChart *ag_chart_new_from_db_save(AgDbSave *save_data, GError **err);
void ag_chart_save_to_file(AgChart *chart, void ag_chart_save_to_file(AgChart *chart,
GFile *file, GFile *file,
GError **err); GError **err);
@ -79,6 +86,8 @@ void ag_chart_set_note(AgChart *chart, const gchar *note);
const gchar *ag_chart_get_note(AgChart *chart); const gchar *ag_chart_get_note(AgChart *chart);
AgDbSave *ag_chart_get_db_save(AgChart *chart, gint db_id);
#define AG_CHART_ERROR (ag_chart_error_quark()) #define AG_CHART_ERROR (ag_chart_error_quark())
GQuark ag_chart_error_quark(void); GQuark ag_chart_error_quark(void);

1134
src/ag-db.c Normal file

File diff suppressed because it is too large Load Diff

78
src/ag-db.h Normal file
View File

@ -0,0 +1,78 @@
#ifndef __AG_DB_H__
#define __AG_DB_H__
#include <glib-object.h>
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define AG_TYPE_DB (ag_db_get_type())
#define AG_DB(o) (G_TYPE_CHECK_INSTANCE_CAST((o), \
AG_TYPE_DB, \
AgDb))
#define AG_DB_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), \
AG_TYPE_DB, \
AgDbClass))
#define AG_IS_DB(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), AG_TYPE_DB))
#define AG_IS_DB_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), AG_TYPE_DB))
#define AG_DB_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), \
AG_TYPE_DB, \
AgDbClass))
typedef struct _AgDb AgDb;
typedef struct _AgDbClass AgDbClass;
struct _AgDb {
GObject parent_instance;
};
struct _AgDbClass {
GObjectClass parent_class;
};
typedef struct _AgDbSave {
gint db_id;
gchar *name;
gchar *country;
gchar *city;
gdouble longitude;
gdouble latitude;
gdouble altitude;
gint year;
guint month;
guint day;
guint hour;
guint minute;
guint second;
gdouble timezone;
gchar *house_system;
gchar *note;
} AgDbSave;
typedef enum {
AG_DB_ERROR_NO_CHART,
AG_DB_ERROR_DATABASE_ERROR,
} AgDbError;
GType ag_db_get_type(void) G_GNUC_CONST;
AgDb *ag_db_get(void);
void ag_db_save_data_free(AgDbSave *save_data);
gboolean ag_db_save_chart(AgDb *db,
AgDbSave *save_data,
GError **err);
GList *ag_db_get_chart_list(AgDb *db, GError **err);
AgDbSave *ag_db_get_chart_data_by_id(AgDb *db, guint row_id, GError **err);
gboolean ag_db_save_identical(const AgDbSave *a, const AgDbSave *b);
#define AG_DB_ERROR (ag_db_error_quark())
GQuark ag_db_error_quark(void);
G_END_DECLS
#endif /* __AG_DB_H__ */

View File

@ -4,6 +4,7 @@
#include <libxml/parser.h> #include <libxml/parser.h>
#include <libxml/tree.h> #include <libxml/tree.h>
#include <webkit2/webkit2.h> #include <webkit2/webkit2.h>
#include <libgd/gd-main-view.h>
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include <swe-glib.h> #include <swe-glib.h>
@ -12,9 +13,12 @@
#include "ag-window.h" #include "ag-window.h"
#include "ag-chart.h" #include "ag-chart.h"
#include "ag-settings.h" #include "ag-settings.h"
#include "ag-db.h"
struct _AgWindowPrivate { struct _AgWindowPrivate {
GtkWidget *header_bar; GtkWidget *header_bar;
GtkWidget *menubutton_revealer;
GtkWidget *new_back_stack;
GtkWidget *stack; GtkWidget *stack;
GtkWidget *name; GtkWidget *name;
GtkWidget *north_lat; GtkWidget *north_lat;
@ -32,6 +36,7 @@ struct _AgWindowPrivate {
GtkWidget *timezone; GtkWidget *timezone;
GtkWidget *house_system; GtkWidget *house_system;
GtkWidget *tab_list;
GtkWidget *tab_chart; GtkWidget *tab_chart;
GtkWidget *tab_edit; GtkWidget *tab_edit;
GtkWidget *current_tab; GtkWidget *current_tab;
@ -42,10 +47,11 @@ struct _AgWindowPrivate {
AgSettings *settings; AgSettings *settings;
AgChart *chart; AgChart *chart;
gchar *uri;
gboolean aspect_table_populated; gboolean aspect_table_populated;
GtkTextBuffer *note_buffer; GtkTextBuffer *note_buffer;
GtkListStore *house_system_model; GtkListStore *house_system_model;
GtkListStore *db_chart_data;
AgDbSave *saved_data;
}; };
G_DEFINE_QUARK(ag_window_error_quark, ag_window_error); G_DEFINE_QUARK(ag_window_error_quark, ag_window_error);
@ -86,19 +92,101 @@ ag_window_view_menu_action(GSimpleAction *action,
g_variant_unref(state); g_variant_unref(state);
} }
gboolean
ag_window_can_close(AgWindow *window, gboolean display_dialog)
{
AgWindowPrivate *priv = ag_window_get_instance_private(window);
gint db_id = (priv->saved_data)
? priv->saved_data->db_id
: -1;
AgDbSave *save_data = NULL;
AgDb *db = ag_db_get();
GError *err = NULL;
gboolean ret = TRUE;
if (priv->chart) {
save_data = ag_chart_get_db_save(priv->chart, db_id);
if (
!ag_db_save_identical(priv->saved_data, save_data)
|| !(priv->saved_data)
|| (priv->saved_data->db_id == -1)
) {
g_debug("Save is needed!");
if (display_dialog) {
gint response;
response = ag_app_buttoned_dialog(
GTK_WIDGET(window),
GTK_MESSAGE_QUESTION,
_("Chart is not saved. Do you want to save it?"),
_("Save and close"), GTK_RESPONSE_YES,
_("Close without saving"), GTK_RESPONSE_NO,
_("Return to chart"), GTK_RESPONSE_CANCEL,
NULL
);
switch (response) {
case GTK_RESPONSE_YES:
if (!ag_db_save_chart(db, save_data, &err)) {
ag_app_message_dialog(
GTK_WIDGET(window),
GTK_MESSAGE_ERROR,
"Unable to save chart: %s",
err->message
);
ret = FALSE;
} else {
ret = TRUE;
}
break;
case GTK_RESPONSE_NO:
ret = TRUE;
break;
default:
ret = FALSE;
break;
}
} else {
ret = FALSE;
}
}
}
ag_db_save_data_free(save_data);
return ret;
}
gboolean
ag_window_delete_event_callback(AgWindow *window,
GdkEvent *event,
gpointer user_data)
{
return (!ag_window_can_close(window, TRUE));
}
static void static void
ag_window_close_action(GSimpleAction *action, ag_window_close_action(GSimpleAction *action,
GVariant *parameter, GVariant *parameter,
gpointer user_data) gpointer user_data)
{ {
AgWindow *window = user_data; AgWindow *window = AG_WINDOW(user_data);
// TODO: Save unsaved changes! if (ag_window_can_close(window, TRUE)) {
gtk_widget_destroy(GTK_WIDGET(window)); gtk_widget_destroy(GTK_WIDGET(window));
}
} }
static void static void
ag_window_save_as(AgWindow *window, GError **err) ag_window_export(AgWindow *window, GError **err)
{ {
gchar *name; gchar *name;
gchar *file_name; gchar *file_name;
@ -146,7 +234,7 @@ ag_window_save_as(AgWindow *window, GError **err)
file_name = g_strdup_printf("%s.agc", name); file_name = g_strdup_printf("%s.agc", name);
g_free(name); g_free(name);
fs = gtk_file_chooser_dialog_new(_("Save Chart"), fs = gtk_file_chooser_dialog_new(_("Export Chart"),
GTK_WINDOW(window), GTK_WINDOW(window),
GTK_FILE_CHOOSER_ACTION_SAVE, GTK_FILE_CHOOSER_ACTION_SAVE,
_("_Cancel"), GTK_RESPONSE_CANCEL, _("_Cancel"), GTK_RESPONSE_CANCEL,
@ -175,34 +263,35 @@ ag_window_save_action(GSimpleAction *action,
GVariant *parameter, GVariant *parameter,
gpointer user_data) gpointer user_data)
{ {
gchar *uri;
AgWindow *window = AG_WINDOW(user_data); AgWindow *window = AG_WINDOW(user_data);
GError *err = NULL;
AgWindowPrivate *priv = ag_window_get_instance_private(window); AgWindowPrivate *priv = ag_window_get_instance_private(window);
AgDb *db = ag_db_get();
GError *err;
gint old_id;
AgDbSave *save_data;
recalculate_chart(window); recalculate_chart(window);
uri = ag_window_get_uri(window);
if (uri != NULL) { if (!ag_window_can_close(window, FALSE)) {
GFile *file = g_file_new_for_uri(uri); old_id = (priv->saved_data) ? priv->saved_data->db_id : -1;
g_free(uri); save_data = ag_chart_get_db_save(priv->chart, old_id);
ag_chart_save_to_file(priv->chart, file, &err); if (!ag_db_save_chart(db, save_data, &err)) {
} else {
ag_window_save_as(window, &err);
}
if (err) {
ag_app_message_dialog( ag_app_message_dialog(
GTK_WIDGET(window), GTK_WIDGET(window),
GTK_MESSAGE_ERROR, GTK_MESSAGE_ERROR,
"%s", err->message _("Unable to save: %s"),
err->message
); );
} }
ag_db_save_data_free(priv->saved_data);
priv->saved_data = save_data;
}
} }
static void static void
ag_window_save_as_action(GSimpleAction *action, ag_window_export_action(GSimpleAction *action,
GVariant *parameter, GVariant *parameter,
gpointer user_data) gpointer user_data)
{ {
@ -210,7 +299,7 @@ ag_window_save_as_action(GSimpleAction *action,
GError *err = NULL; GError *err = NULL;
recalculate_chart(window); recalculate_chart(window);
ag_window_save_as(window, &err); ag_window_export(window, &err);
if (err) { if (err) {
ag_app_message_dialog( ag_app_message_dialog(
@ -874,6 +963,14 @@ ag_window_tab_changed_cb(GtkStack *stack, GParamSpec *pspec, AgWindow *window)
gtk_widget_set_size_request(active_tab, 600, 600); gtk_widget_set_size_request(active_tab, 600, 600);
} }
if (strcmp("list", active_tab_name) == 0) {
gtk_revealer_set_reveal_child(GTK_REVEALER(priv->menubutton_revealer), FALSE);
gtk_stack_set_visible_child_name(GTK_STACK(priv->new_back_stack), "new");
} else {
gtk_revealer_set_reveal_child(GTK_REVEALER(priv->menubutton_revealer), TRUE);
gtk_stack_set_visible_child_name(GTK_STACK(priv->new_back_stack), "back");
}
// If we are coming from the Edit tab, lets assume the chart data has // If we are coming from the Edit tab, lets assume the chart data has
// changed. This is a bad idea, though, it should be checked instead! // changed. This is a bad idea, though, it should be checked instead!
// (TODO) // (TODO)
@ -900,14 +997,68 @@ ag_window_change_tab_action(GSimpleAction *action,
g_action_change_state(G_ACTION(action), parameter); g_action_change_state(G_ACTION(action), parameter);
} }
static void
ag_window_new_chart_action(GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
AgWindow *window = AG_WINDOW(user_data);
AgWindowPrivate *priv = ag_window_get_instance_private(window);
if (priv->chart) {
ag_app_message_dialog(
GTK_WIDGET(window),
GTK_MESSAGE_ERROR,
"This window already has a chart. " \
"This should not happen, " \
"please consider issuing a bug report!"
);
gtk_stack_set_visible_child_name(GTK_STACK(priv->stack), "chart");
return;
}
gtk_stack_set_visible_child_name(GTK_STACK(priv->stack), "edit");
}
static void
ag_window_back_action(GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
AgWindow *window = AG_WINDOW(user_data);
AgWindowPrivate *priv = ag_window_get_instance_private(window);
if (ag_window_can_close(window, TRUE)) {
g_clear_object(&(priv->chart));
ag_db_save_data_free(priv->saved_data);
priv->saved_data = NULL;
ag_window_load_chart_list(window);
gtk_stack_set_visible_child_name(GTK_STACK(priv->stack), "list");
}
}
static void
ag_window_refresh_action(GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
ag_window_load_chart_list(AG_WINDOW(user_data));
}
static GActionEntry win_entries[] = { static GActionEntry win_entries[] = {
{ "close", ag_window_close_action, NULL, NULL, NULL }, { "close", ag_window_close_action, NULL, NULL, NULL },
{ "save", ag_window_save_action, NULL, NULL, NULL }, { "save", ag_window_save_action, NULL, NULL, NULL },
{ "save-as", ag_window_save_as_action, NULL, NULL, NULL }, { "export", ag_window_export_action, NULL, NULL, NULL },
{ "export-svg", ag_window_export_svg_action, NULL, NULL, NULL }, { "export-svg", ag_window_export_svg_action, NULL, NULL, NULL },
{ "view-menu", ag_window_view_menu_action, NULL, "false", NULL }, { "view-menu", ag_window_view_menu_action, NULL, "false", NULL },
{ "gear-menu", ag_window_gear_menu_action, NULL, "false", NULL }, { "gear-menu", ag_window_gear_menu_action, NULL, "false", NULL },
{ "change-tab", ag_window_change_tab_action, "s", "'edit'", NULL }, { "change-tab", ag_window_change_tab_action, "s", "'edit'", NULL },
{ "new-chart", ag_window_new_chart_action, NULL, NULL, NULL },
{ "back", ag_window_back_action, NULL, NULL, NULL },
{ "refresh", ag_window_refresh_action, NULL, NULL, NULL },
}; };
static void static void
@ -972,6 +1123,70 @@ ag_window_set_default_house_system(GtkTreeModel *model,
return FALSE; return FALSE;
} }
static void
ag_window_list_item_activated_cb(GdMainView *view,
const gchar *id,
const GtkTreePath *path,
AgWindow *window)
{
guint row_id = atoi(id);
AgWindowPrivate *priv = ag_window_get_instance_private(window);
AgDb *db = ag_db_get();
GError *err = NULL;
if (priv->saved_data != NULL) {
ag_app_message_dialog(
GTK_WIDGET(window),
GTK_MESSAGE_ERROR,
"Window chart is not saved. " \
"This is a bug, it should not happen here. " \
"Please consider opening a bug report!"
);
ag_window_change_tab(window, "chart");
return;
}
if ((priv->saved_data = ag_db_get_chart_data_by_id(
db,
row_id,
&err)) == NULL) {
ag_app_message_dialog(
GTK_WIDGET(window),
GTK_MESSAGE_ERROR,
"Could not open chart."
);
return;
}
if (priv->chart) {
g_object_unref(priv->chart);
priv->chart = NULL;
}
if ((priv->chart = ag_chart_new_from_db_save(
priv->saved_data,
&err
)) == NULL) {
ag_app_message_dialog(
GTK_WIDGET(window),
GTK_MESSAGE_ERROR,
"Error: %s",
err->message
);
ag_db_save_data_free(priv->saved_data);
priv->saved_data = NULL;
return;
}
ag_window_update_from_chart(window);
ag_window_change_tab(window, "chart");
}
static void static void
ag_window_init(AgWindow *window) ag_window_init(AgWindow *window)
{ {
@ -1021,8 +1236,29 @@ ag_window_init(AgWindow *window)
NULL NULL
); );
gtk_stack_set_visible_child_name(GTK_STACK(priv->stack), "edit"); priv->tab_list = GTK_WIDGET(gd_main_view_new(GD_MAIN_VIEW_ICON));
priv->current_tab = priv->tab_edit; gtk_stack_add_titled(
GTK_STACK(priv->stack),
priv->tab_list,
"list",
"Chart list"
);
gd_main_view_set_selection_mode(GD_MAIN_VIEW(priv->tab_list), FALSE);
gd_main_view_set_model(
GD_MAIN_VIEW(priv->tab_list),
GTK_TREE_MODEL(priv->db_chart_data)
);
g_signal_connect(
priv->tab_list,
"item-activated",
G_CALLBACK(ag_window_list_item_activated_cb),
window
);
gtk_stack_set_visible_child_name(GTK_STACK(priv->stack), "list");
priv->current_tab = priv->tab_list;
g_object_set( g_object_set(
priv->year_adjust, priv->year_adjust,
"lower", (gdouble)G_MININT, "lower", (gdouble)G_MININT,
@ -1031,7 +1267,6 @@ ag_window_init(AgWindow *window)
); );
priv->chart = NULL; priv->chart = NULL;
priv->uri = NULL;
g_action_map_add_action_entries( g_action_map_add_action_entries(
G_ACTION_MAP(window), G_ACTION_MAP(window),
@ -1071,6 +1306,21 @@ ag_window_class_init(AgWindowClass *klass)
AgWindow, AgWindow,
header_bar header_bar
); );
gtk_widget_class_bind_template_child_private(
widget_class,
AgWindow,
new_back_stack
);
gtk_widget_class_bind_template_child_private(
widget_class,
AgWindow,
menubutton_revealer
);
gtk_widget_class_bind_template_child_private(
widget_class,
AgWindow,
db_chart_data
);
gtk_widget_class_bind_template_child_private(widget_class, AgWindow, name); gtk_widget_class_bind_template_child_private(widget_class, AgWindow, name);
gtk_widget_class_bind_template_child_private(widget_class, AgWindow, year); gtk_widget_class_bind_template_child_private(widget_class, AgWindow, year);
gtk_widget_class_bind_template_child_private(widget_class, AgWindow, month); gtk_widget_class_bind_template_child_private(widget_class, AgWindow, month);
@ -1236,9 +1486,12 @@ ag_window_set_chart(AgWindow *window, AgChart *chart)
g_clear_object(&(priv->chart)); g_clear_object(&(priv->chart));
} }
ag_db_save_data_free(priv->saved_data);
priv->chart = chart; priv->chart = chart;
g_signal_connect(priv->chart, "changed", G_CALLBACK(chart_changed), window); g_signal_connect(priv->chart, "changed", G_CALLBACK(chart_changed), window);
g_object_ref(chart); g_object_ref(chart);
priv->saved_data = ag_chart_get_db_save(chart, -1);
} }
AgChart * AgChart *
@ -1249,26 +1502,6 @@ ag_window_get_chart(AgWindow *window)
return priv->chart; return priv->chart;
} }
void
ag_window_set_uri(AgWindow *window, const gchar *uri)
{
AgWindowPrivate *priv = ag_window_get_instance_private(window);
if (priv->uri != NULL) {
g_free(priv->uri);
}
priv->uri = g_strdup(uri);
}
gchar *
ag_window_get_uri(AgWindow *window)
{
AgWindowPrivate *priv = ag_window_get_instance_private(window);
return g_strdup(priv->uri);
}
void void
ag_window_settings_restore(GtkWindow *window, GSettings *settings) ag_window_settings_restore(GtkWindow *window, GSettings *settings)
{ {
@ -1343,3 +1576,51 @@ ag_window_name_changed_cb(GtkEntry *name_entry, AgWindow *window)
gtk_header_bar_set_subtitle(GTK_HEADER_BAR(priv->header_bar), name); gtk_header_bar_set_subtitle(GTK_HEADER_BAR(priv->header_bar), name);
} }
static void
ag_window_add_chart_to_list(AgDbSave *save_data, AgWindow *window)
{
GtkTreeIter iter;
AgWindowPrivate *priv = ag_window_get_instance_private(window);
gchar *id = g_strdup_printf("%d", save_data->db_id);
gtk_list_store_append(priv->db_chart_data, &iter);
gtk_list_store_set(
priv->db_chart_data, &iter,
0, id, /* ID */
1, NULL, /* URI */
2, save_data->name, /* Primary text */
3, NULL, /* Secondary text */
4, NULL, /* Icon */
5, 0, /* mtime */
6, FALSE, /* Selected */
7, 0, /* Pulse */
-1
);
g_free(id);
}
static void
ag_window_clear_chart_list(AgWindow *window)
{
AgWindowPrivate *priv = ag_window_get_instance_private(window);
gtk_list_store_clear(priv->db_chart_data);
}
gboolean
ag_window_load_chart_list(AgWindow *window)
{
AgDb *db = ag_db_get();
GError *err = NULL;
GList *chart_list = ag_db_get_chart_list(db, &err);
ag_window_clear_chart_list(window);
/* With only a few charts, this should be fine. Maybe implementing lazy
* loading would be a better idea. See:
* http://blogs.gnome.org/ebassi/documentation/lazy-loading/
*/
g_list_foreach(chart_list, (GFunc)ag_window_add_chart_to_list, window);
return TRUE;
}

View File

@ -50,11 +50,6 @@ AgChart *ag_window_get_chart(AgWindow *window);
void ag_window_update_from_chart(AgWindow *window); void ag_window_update_from_chart(AgWindow *window);
void ag_window_set_uri(AgWindow *window,
const gchar *uri);
gchar *ag_window_get_uri(AgWindow *window);
void ag_window_settings_restore(GtkWindow *window, void ag_window_settings_restore(GtkWindow *window,
GSettings *settings); GSettings *settings);
@ -63,6 +58,8 @@ void ag_window_settings_save(GtkWindow *window,
void ag_window_change_tab(AgWindow *window, const gchar *tab_name); void ag_window_change_tab(AgWindow *window, const gchar *tab_name);
gboolean ag_window_load_chart_list(AgWindow *window);
#define AG_WINDOW_ERROR (ag_window_error_quark()) #define AG_WINDOW_ERROR (ag_window_error_quark())
GQuark ag_window_error_quark(void); GQuark ag_window_error_quark(void);

View File

@ -38,9 +38,9 @@
<attribute name="accel">&lt;Primary&gt;s</attribute> <attribute name="accel">&lt;Primary&gt;s</attribute>
</item> </item>
<item> <item>
<attribute name="label" translatable="yes">Save as…</attribute> <attribute name="label" translatable="yes">Export…</attribute>
<attribute name="action">win.save-as</attribute> <attribute name="action">win.export</attribute>
<attribute name="accel">&lt;Primary&gt;&lt;Shift&gt;s</attribute> <attribute name="accel">&lt;Primary&gt;&lt;Shift&gt;e</attribute>
</item> </item>
</section> </section>
<section> <section>
@ -116,11 +116,32 @@
</object> </object>
<object class="GtkTextBuffer" id="note_buffer"> <object class="GtkTextBuffer" id="note_buffer">
</object> </object>
<object class="GtkListStore" id="db_chart_data">
<columns>
<!-- column-name id -->
<column type="gchararray"/>
<!-- column-name uri -->
<column type="gchararray"/>
<!-- column-name name -->
<column type="gchararray"/>
<!-- column-name secondary-text -->
<column type="gchararray"/>
<!-- column-name icon -->
<column type="GdkPixbuf"/>
<!-- column-name mtime -->
<column type="gint64"/>
<!-- column-name selected -->
<column type="gboolean"/>
<!-- column-name pulse -->
<column type="guint"/>
</columns>
</object>
<template class="AgWindow" parent="GtkApplicationWindow"> <template class="AgWindow" parent="GtkApplicationWindow">
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="has_focus">False</property> <property name="has_focus">False</property>
<property name="is_focus">False</property> <property name="is_focus">False</property>
<property name="icon_name">astrognome</property> <property name="icon_name">astrognome</property>
<signal name="delete-event" handler="ag_window_delete_event_callback" swapped="no"/>
<child type="titlebar"> <child type="titlebar">
<object class="GtkHeaderBar" id="header_bar"> <object class="GtkHeaderBar" id="header_bar">
<property name="visible">True</property> <property name="visible">True</property>
@ -128,6 +149,82 @@
<property name="vexpand">False</property> <property name="vexpand">False</property>
<property name="show_close_button">True</property> <property name="show_close_button">True</property>
<property name="title" translatable="yes">Astrognome</property> <property name="title" translatable="yes">Astrognome</property>
<child>
<object class="GtkStack" id="new_back_stack">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="homogeneous">False</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkButton" id="new_button">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">win.new-chart</property>
<style>
<class name="image-button"/>
</style>
<child>
<object class="GtkImage" id="new_image">
<property name="visible">True</property>
<property name="icon_size">1</property>
<property name="icon_name">document-new-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="refresh_button">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">win.refresh</property>
<style>
<class name="image-button"/>
</style>
<child>
<object class="GtkImage" id="refresh_image">
<property name="visible">True</property>
<property name="icon_size">1</property>
<property name="icon_name">view-refresh-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="name">new</property>
</packing>
</child>
<child>
<object class="GtkButton" id="back_button">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="action_name">win.back</property>
<style>
<class name="image-button"/>
</style>
<child>
<object class="GtkImage" id="back_image">
<property name="visible">True</property>
<property name="icon_size">1</property>
<property name="icon_name">go-previous-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="name">back</property>
</packing>
</child>
</object>
</child>
<child> <child>
<object class="GtkBox" id="box"> <object class="GtkBox" id="box">
<property name="visible">True</property> <property name="visible">True</property>
@ -138,6 +235,38 @@
</style> </style>
</object> </object>
</child> </child>
<child>
<object class="GtkRevealer" id="menubutton_revealer">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="reveal_child">False</property>
<child>
<object class="GtkBox" id="menubutton_box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkMenuButton">
<property name="visible">True</property>
<property name="valign">center</property>
<property name="can_focus">False</property>
<property name="action_name">win.view-menu</property>
<property name="menu_model">view_menu</property>
<property name="use_popover">False</property>
<style>
<class name="image-button"/>
</style>
<child>
<object class="GtkImage" id="view_image">
<property name="visible">True</property>
<property name="icon_size">1</property>
<property name="icon_name">document-properties-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="position">0</property>
</packing>
</child>
<child> <child>
<object class="GtkMenuButton"> <object class="GtkMenuButton">
<property name="visible">True</property> <property name="visible">True</property>
@ -158,25 +287,9 @@
</child> </child>
</object> </object>
<packing> <packing>
<property name="pack_type">end</property> <property name="position">1</property>
</packing> </packing>
</child> </child>
<child>
<object class="GtkMenuButton">
<property name="visible">True</property>
<property name="valign">center</property>
<property name="can_focus">False</property>
<property name="action_name">win.view-menu</property>
<property name="menu_model">view_menu</property>
<property name="use_popover">False</property>
<style>
<class name="image-button"/>
</style>
<child>
<object class="GtkImage" id="view_image">
<property name="visible">True</property>
<property name="icon_size">1</property>
<property name="icon_name">document-properties-symbolic</property>
</object> </object>
</child> </child>
</object> </object>

View File

@ -9,8 +9,8 @@
<attribute name="accel">&lt;Primary&gt;n</attribute> <attribute name="accel">&lt;Primary&gt;n</attribute>
</item> </item>
<item> <item>
<attribute name="label" translatable="yes">Open</attribute> <attribute name="label" translatable="yes">Import</attribute>
<attribute name="action">app.open</attribute> <attribute name="action">app.import</attribute>
<attribute name="accel">&lt;Primary&gt;o</attribute> <attribute name="accel">&lt;Primary&gt;o</attribute>
</item> </item>
</section> </section>