diff --git a/.gitignore b/.gitignore
index d48371f..499497f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,8 +40,8 @@ Makefile.in
/data/astrognome.desktop.in
# Generated files
-/src/ag-resources.c
-/src/ag-resources.h
+/src/ag-resources.[ch]
+/src/ag-enumtypes.[ch]
# Geonames related things
/data/geonames/*.txt
diff --git a/src/Makefile.am b/src/Makefile.am
index c39ca17..3f9c4f1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,15 +1,27 @@
RESOURCE_DIR = $(srcdir)/resources
resource_files = $(shell glib-compile-resources --sourcedir=$(RESOURCE_DIR) --generate-dependencies $(srcdir)/ag.gresource.xml)
+ag_enum_headers = ag-icon-view.h
+
ag-resources.c: ag.gresource.xml $(resource_files)
glib-compile-resources --target=$@ --sourcedir=$(RESOURCE_DIR) --generate-source --c-name ag $(srcdir)/ag.gresource.xml
ag-resources.h: ag.gresource.xml $(resource_files)
glib-compile-resources --target=$@ --sourcedir=$(RESOURCE_DIR) --generate-header --c-name ag $(srcdir)/ag.gresource.xml
+ag-enumtypes.h: $(ag_enum_headers) ag-enumtypes.h.template
+ $(GLIB_MKENUMS) --template $(filter %.template,$^) $(filter-out %.template,$^) > \
+ ag-enumtypes.h.tmp && mv ag-enumtypes.h.tmp ag-enumtypes.h
+
+ag-enumtypes.c: $(ag_enum_headers) ag-enumtypes.c.template
+ $(GLIB_MKENUMS) --template $(filter %.template,$^) $(filter-out %.template,$^) > \
+ ag-enumtypes.c.tmp && mv ag-enumtypes.c.tmp ag-enumtypes.c
+
BUILT_SOURCES = \
ag-resources.h \
ag-resources.c \
+ ag-enumtypes.h \
+ ag-enumtypes.c \
$(NULL)
astrognome_source_files = \
@@ -20,6 +32,7 @@ astrognome_source_files = \
ag-preferences.c \
ag-db.c \
ag-display-theme.c \
+ ag-icon-view.c \
ag-chart-renderer.c \
astrognome.c \
$(NULL)
diff --git a/src/ag-enumtypes.c.template b/src/ag-enumtypes.c.template
new file mode 100644
index 0000000..70758d9
--- /dev/null
+++ b/src/ag-enumtypes.c.template
@@ -0,0 +1,63 @@
+/*** BEGIN file-header ***/
+/* ag-enumtypes.c - Enumeration types for Astrognome
+ *
+ * Copyright © 2013 Gergely Polonkai
+ *
+ * Astrognome 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.
+ *
+ * Astrognome 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 this library; if not, see .
+ */
+
+#include "ag-enumtypes.h"
+#include "@filename@"
+
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType
+@enum_name@_get_type(void)
+{
+ static volatile gsize g_define_type_id__volatile = 0;
+
+ if (g_once_init_enter(&g_define_type_id__volatile)) {
+ static const G@Type@Value values[] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+ {
+ @VALUENAME@,
+ "@VALUENAME@",
+ "@valuenick@"
+ },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+ { 0, NULL, NULL }
+ };
+
+ GType g_define_type_id = g_@type@_register_static(
+ g_intern_static_string("@EnumName@"),
+ values
+ );
+
+ g_once_init_leave(&g_define_type_id__volatile, g_define_type_id);
+ }
+
+ return g_define_type_id__volatile;
+}
+
+/*** END value-tail ***/
+
diff --git a/src/ag-enumtypes.h.template b/src/ag-enumtypes.h.template
new file mode 100644
index 0000000..a89ed67
--- /dev/null
+++ b/src/ag-enumtypes.h.template
@@ -0,0 +1,42 @@
+/*** BEGIN file-header ***/
+/* ag-enumtypes.h - Enumeration types for Astrognome
+ *
+ * Copyright © 2013 Gergely Polonkai
+ *
+ * Astrognome 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.
+ *
+ * Astrognome 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 this library; if not, see .
+ */
+#ifndef __AG_ENUM_TYPES_H__
+#define __AG_ENUM_TYPES_H__
+
+#include
+
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+
+/* enumerations from "@filename@" */
+
+#include "@filename@"
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType @enum_name@_get_type(void);
+#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type())
+/*** END value-header ***/
+
+/*** BEGIN file-tail ***/
+
+#endif /* __AG_ENUM_TYPES_H__ */
+/*** END file-tail ***/
+
diff --git a/src/ag-icon-view.c b/src/ag-icon-view.c
new file mode 100644
index 0000000..bde569a
--- /dev/null
+++ b/src/ag-icon-view.c
@@ -0,0 +1,423 @@
+#include "ag-enumtypes.h"
+#include "ag-icon-view.h"
+#include "ag-db.h"
+#include "ag-chart-renderer.h"
+
+typedef struct _AgIconViewPrivate {
+ AgIconViewMode mode;
+ AgChartRenderer *thumb_renderer;
+ GtkCellRenderer *text_renderer;
+ GtkListStore *model;
+} AgIconViewPrivate;
+
+enum {
+ PROP_0,
+ PROP_MODE,
+ PROP_LAST
+};
+
+enum {
+ AG_ICON_VIEW_COLUMN_SELECTED,
+ AG_ICON_VIEW_COLUMN_ITEM,
+ AG_ICON_VIEW_COLUMN_COLUMNS
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE(AgIconView, ag_icon_view, GTK_TYPE_ICON_VIEW);
+
+static GParamSpec *properties[PROP_LAST];
+
+void
+ag_icon_view_set_mode(AgIconView *icon_view, AgIconViewMode mode)
+{
+ AgIconViewPrivate *priv = ag_icon_view_get_instance_private(icon_view);
+
+ if (priv->mode != mode) {
+ priv->mode = mode;
+
+ if (mode != AG_ICON_VIEW_MODE_SELECTION) {
+ ag_icon_view_unselect_all(icon_view);
+ }
+
+ ag_chart_renderer_set_toggle_visible(priv->thumb_renderer, (mode == AG_ICON_VIEW_MODE_SELECTION));
+
+ gtk_widget_queue_draw(GTK_WIDGET(icon_view));
+
+ g_object_notify_by_pspec(
+ G_OBJECT(icon_view),
+ properties[PROP_MODE]
+ );
+ }
+}
+
+AgIconViewMode
+ag_icon_view_get_mode(AgIconView *icon_view)
+{
+ AgIconViewPrivate *priv = ag_icon_view_get_instance_private(icon_view);
+
+ return priv->mode;
+}
+
+static void
+ag_icon_view_set_property(GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *param_spec)
+{
+ switch (prop_id) {
+ case PROP_MODE:
+ ag_icon_view_set_mode(
+ AG_ICON_VIEW(gobject),
+ g_value_get_enum(value)
+ );
+
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
+
+ break;
+ }
+}
+
+static void
+ag_icon_view_get_property(GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *param_spec)
+{
+ AgIconViewPrivate *priv = ag_icon_view_get_instance_private(
+ AG_ICON_VIEW(gobject)
+ );
+
+ switch (prop_id) {
+ case PROP_MODE:
+ g_value_set_enum(value, priv->mode);
+
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, param_spec);
+
+ break;
+ }
+}
+
+static void
+ag_icon_view_selection_changed(AgIconView *icon_view)
+{
+ g_signal_emit_by_name(icon_view, "selection-changed");
+}
+
+static void
+ag_icon_view_item_activated(AgIconView *icon_view, GtkTreePath *path)
+{
+ g_signal_emit_by_name(icon_view, "item-activated", path);
+}
+
+static gboolean
+ag_icon_view_button_press_event_cb(GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GtkTreePath *path;
+ GtkIconView *gtk_icon_view = GTK_ICON_VIEW(widget);
+ AgIconView *ag_icon_view = AG_ICON_VIEW(widget);
+
+ path = gtk_icon_view_get_path_at_pos(gtk_icon_view, ((GdkEventButton *)event)->x, ((GdkEventButton *)event)->y);
+
+ if (path != NULL) {
+ gboolean selected;
+ AgDbChartSave *chart_save;
+ GtkListStore *store = GTK_LIST_STORE(gtk_icon_view_get_model(gtk_icon_view));
+
+ if (event->button == GDK_BUTTON_SECONDARY) {
+ ag_icon_view_set_mode(ag_icon_view, AG_ICON_VIEW_MODE_SELECTION);
+ }
+
+ if (ag_icon_view_get_mode(ag_icon_view) == AG_ICON_VIEW_MODE_SELECTION) {
+ GtkTreeIter iter;
+
+ if (gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path)) {
+ gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, AG_ICON_VIEW_COLUMN_SELECTED, &selected, AG_ICON_VIEW_COLUMN_ITEM, &chart_save, -1);
+
+ gtk_list_store_set(store, &iter, AG_ICON_VIEW_COLUMN_SELECTED, !selected, -1);
+
+ ag_icon_view_selection_changed(ag_icon_view);
+ }
+ } else {
+ ag_icon_view_item_activated(ag_icon_view, path);
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+ag_icon_view_class_init(AgIconViewClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+
+ gobject_class->set_property = ag_icon_view_set_property;
+ gobject_class->get_property = ag_icon_view_get_property;
+ widget_class->button_press_event = ag_icon_view_button_press_event_cb;
+
+ properties[PROP_MODE] = g_param_spec_enum(
+ "mode",
+ "Mode",
+ "Mode",
+ AG_TYPE_ICON_VIEW_MODE,
+ AG_ICON_VIEW_MODE_NORMAL,
+ G_PARAM_STATIC_NAME
+ | G_PARAM_STATIC_NICK
+ | G_PARAM_STATIC_BLURB
+ | G_PARAM_READABLE
+ | G_PARAM_WRITABLE
+ );
+ g_object_class_install_property(
+ gobject_class,
+ PROP_MODE,
+ properties[PROP_MODE]
+ );
+}
+
+static void
+ag_icon_view_chart_renderer_func(GtkCellLayout *layout,
+ GtkCellRenderer *renderer,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ AgIconView *icon_view)
+{
+ AgDbChartSave *chart_save;
+
+ gtk_tree_model_get(model, iter, AG_ICON_VIEW_COLUMN_ITEM, &chart_save, -1);
+
+ if (chart_save) {
+ g_object_set(renderer, "pixbuf", NULL, NULL);
+ }
+}
+
+static void
+ag_icon_view_text_renderer_func(GtkCellLayout *layout,
+ GtkCellRenderer *renderer,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ AgIconView *icon_view)
+{
+ AgDbChartSave *chart_save;
+
+ gtk_tree_model_get(model, iter, AG_ICON_VIEW_COLUMN_ITEM, &chart_save, -1);
+
+ if (chart_save) {
+ gchar *text;
+
+ text = g_markup_escape_text(chart_save->name, -1);
+ g_object_set(renderer, "markup", text, NULL);
+ g_free(text);
+ }
+}
+
+static void
+ag_icon_view_init(AgIconView *icon_view)
+{
+ AgIconViewPrivate *priv = ag_icon_view_get_instance_private(icon_view);
+ guint tile_width,
+ tile_height;
+
+ priv->model = gtk_list_store_new(
+ AG_ICON_VIEW_COLUMN_COLUMNS,
+ G_TYPE_BOOLEAN,
+ AG_TYPE_DB_CHART_SAVE
+ );
+ gtk_icon_view_set_model(
+ GTK_ICON_VIEW(icon_view),
+ GTK_TREE_MODEL(priv->model)
+ );
+
+ gtk_icon_view_set_selection_mode(
+ GTK_ICON_VIEW(icon_view),
+ GTK_SELECTION_NONE
+ );
+ priv->mode = AG_ICON_VIEW_MODE_NORMAL;
+
+ gtk_icon_view_set_item_padding(GTK_ICON_VIEW(icon_view), 0);
+ gtk_icon_view_set_margin(GTK_ICON_VIEW(icon_view), 12);
+
+ tile_width = AG_CHART_RENDERER_TILE_SIZE
+ + 2 * AG_CHART_RENDERER_TILE_MARGIN;
+ tile_height = AG_CHART_RENDERER_TILE_SIZE
+ + AG_CHART_RENDERER_TILE_MARGIN
+ + AG_CHART_RENDERER_TILE_MARGIN_BOTTOM;
+
+ priv->thumb_renderer = ag_chart_renderer_new();
+ gtk_cell_renderer_set_alignment(
+ GTK_CELL_RENDERER(priv->thumb_renderer),
+ 0.5, 0.5
+ );
+ gtk_cell_renderer_set_fixed_size(
+ GTK_CELL_RENDERER(priv->thumb_renderer),
+ tile_width, tile_height
+ );
+ gtk_cell_layout_pack_start(
+ GTK_CELL_LAYOUT(icon_view),
+ GTK_CELL_RENDERER(priv->thumb_renderer),
+ FALSE
+ );
+ gtk_cell_layout_add_attribute(
+ GTK_CELL_LAYOUT(icon_view),
+ GTK_CELL_RENDERER(priv->thumb_renderer),
+ "checked", AG_ICON_VIEW_COLUMN_SELECTED
+ );
+ gtk_cell_layout_set_cell_data_func(
+ GTK_CELL_LAYOUT(icon_view),
+ GTK_CELL_RENDERER(priv->thumb_renderer),
+ (GtkCellLayoutDataFunc)ag_icon_view_chart_renderer_func,
+ icon_view,
+ NULL
+ );
+
+ priv->text_renderer = gtk_cell_renderer_text_new();
+ gtk_cell_renderer_set_alignment(
+ GTK_CELL_RENDERER(priv->text_renderer),
+ 0.5, 0.5
+ );
+ gtk_cell_layout_pack_start(
+ GTK_CELL_LAYOUT(icon_view),
+ priv->text_renderer,
+ TRUE
+ );
+ gtk_cell_layout_set_cell_data_func(
+ GTK_CELL_LAYOUT(icon_view),
+ GTK_CELL_RENDERER(priv->text_renderer),
+ (GtkCellLayoutDataFunc)ag_icon_view_text_renderer_func,
+ icon_view,
+ NULL
+ );
+}
+
+void
+ag_icon_view_add_chart(AgIconView *icon_view, AgDbChartSave *chart_save)
+{
+ GtkTreeIter iter;
+ AgIconViewPrivate *priv = ag_icon_view_get_instance_private(icon_view);
+
+ g_debug("Adding chart for %s", chart_save->name);
+
+ gtk_list_store_append(priv->model, &iter);
+ gtk_list_store_set(
+ priv->model, &iter,
+ AG_ICON_VIEW_COLUMN_SELECTED, FALSE,
+ AG_ICON_VIEW_COLUMN_ITEM, chart_save,
+ -1
+ );
+}
+
+static gboolean
+ag_icon_view_check_selected(GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ GList **list)
+{
+ gboolean selected;
+
+ gtk_tree_model_get(model, iter, AG_ICON_VIEW_COLUMN_SELECTED, &selected, -1);
+
+ if (selected) {
+ *list = g_list_prepend(*list, path);
+ }
+
+ return FALSE;
+}
+
+GList *
+ag_icon_view_get_selected_items(AgIconView *icon_view)
+{
+ AgIconViewPrivate *priv = ag_icon_view_get_instance_private(icon_view);
+ GList *items = NULL;
+
+ gtk_tree_model_foreach(GTK_TREE_MODEL(priv->model), (GtkTreeModelForeachFunc)ag_icon_view_check_selected, &items);
+
+ return g_list_reverse(items);
+}
+
+static gboolean
+ag_icon_view_change_item_selection(GtkListStore *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gboolean *selected)
+{
+ gtk_list_store_set(model, iter, AG_ICON_VIEW_COLUMN_SELECTED, *selected, -1);
+
+ return FALSE;
+}
+
+void
+ag_icon_view_select_all(AgIconView *icon_view)
+{
+ AgIconViewPrivate *priv = ag_icon_view_get_instance_private(icon_view);
+ gboolean selected = TRUE;
+
+ gtk_tree_model_foreach(
+ GTK_TREE_MODEL(priv->model),
+ (GtkTreeModelForeachFunc)ag_icon_view_change_item_selection,
+ &selected
+ );
+
+ ag_icon_view_selection_changed(icon_view);
+}
+
+void
+ag_icon_view_unselect_all(AgIconView *icon_view)
+{
+ AgIconViewPrivate *priv = ag_icon_view_get_instance_private(icon_view);
+ gboolean selected = FALSE;
+
+ gtk_tree_model_foreach(
+ GTK_TREE_MODEL(priv->model),
+ (GtkTreeModelForeachFunc)ag_icon_view_change_item_selection,
+ &selected
+ );
+
+ ag_icon_view_selection_changed(icon_view);
+}
+
+AgDbChartSave *
+ag_icon_view_get_chart_save_at_path(AgIconView *icon_view,
+ const GtkTreePath *path)
+{
+ AgIconViewPrivate *priv = ag_icon_view_get_instance_private(icon_view);
+ GtkTreeIter iter;
+ AgDbChartSave *save_data;
+
+ if (gtk_tree_model_get_iter(GTK_TREE_MODEL(priv->model), &iter, (GtkTreePath *)path)) {
+ gtk_tree_model_get(
+ GTK_TREE_MODEL(priv->model), &iter,
+ AG_ICON_VIEW_COLUMN_ITEM, &save_data,
+ -1
+ );
+ } else {
+ g_warning("Invalid tree path");
+
+ save_data = NULL;
+ }
+
+ return save_data;
+}
+
+void
+ag_icon_view_remove_selected(AgIconView *icon_view)
+{
+ AgIconViewPrivate *priv = ag_icon_view_get_instance_private(icon_view);
+ GList *paths = g_list_reverse(ag_icon_view_get_selected_items(icon_view)),
+ *l;
+
+ for (l = paths; l; l = g_list_next(l)) {
+ GtkTreeIter iter;
+ GtkTreePath *path = l->data;
+
+ if (gtk_tree_model_get_iter(GTK_TREE_MODEL(priv->model), &iter, path)) {
+ gtk_list_store_remove(priv->model, &iter);
+ }
+ }
+
+ ag_icon_view_selection_changed(icon_view);
+}
diff --git a/src/ag-icon-view.h b/src/ag-icon-view.h
new file mode 100644
index 0000000..9808a0b
--- /dev/null
+++ b/src/ag-icon-view.h
@@ -0,0 +1,69 @@
+#ifndef __AG_ICON_VIEW_H__
+#define __AG_ICON_VIEW_H__
+
+#include
+
+#include "ag-db.h"
+
+G_BEGIN_DECLS
+
+#define AG_TYPE_ICON_VIEW \
+ (ag_icon_view_get_type())
+#define AG_ICON_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+ AG_TYPE_ICON_VIEW, \
+ AgIconView))
+#define AG_ICON_VIEW_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), \
+ AG_TYPE_ICON_VIEW, \
+ AgIconViewClass))
+#define IS_AG_ICON_VIEW(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+ AG_TYPE_ICON_VIEW))
+#define IS_AG_ICON_VIEW_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+ AG_TYPE_ICON_VIEW))
+#define AG_ICON_VIEW_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+ AG_TYPE_ICON_VIEW, \
+ AgIconViewClass))
+
+typedef struct _AgIconView AgIconView;
+typedef struct _AgIconViewClass AgIconViewClass;
+
+struct _AgIconViewClass
+{
+ GtkIconViewClass parent_class;
+};
+
+struct _AgIconView
+{
+ GtkIconView parent;
+};
+
+typedef enum {
+ AG_ICON_VIEW_MODE_NORMAL,
+ AG_ICON_VIEW_MODE_SELECTION
+} AgIconViewMode;
+
+GType ag_icon_view_get_type (void) G_GNUC_CONST;
+
+void ag_icon_view_set_mode(AgIconView *icon_view, AgIconViewMode mode);
+
+AgIconViewMode ag_icon_view_get_mode(AgIconView *icon_view);
+
+void ag_icon_view_add_chart(AgIconView *icon_view, AgDbChartSave *chart_save);
+
+GList *ag_icon_view_get_selected_items(AgIconView *icon_view);
+
+AgDbChartSave *ag_icon_view_get_chart_save_at_path(AgIconView *icon_view, const GtkTreePath *path);
+
+void ag_icon_view_select_all(AgIconView *icon_view);
+
+void ag_icon_view_unselect_all(AgIconView *icon_view);
+
+void ag_icon_view_remove_selected(AgIconView *icon_view);
+
+G_END_DECLS
+
+#endif /* __AG_ICON_VIEW_H__ */