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__ */