[gnome-todo/wip/gbsneto/plugins] views: implement list view



commit c6ad89599bc79b55fa78e8c02988a0b53afbd829
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date:   Sat Jan 16 19:29:59 2016 -0200

    views: implement list view
    
    It's looking pretty good, actually. I'm getting
    satisfied with the result of it.

 data/Makefile.am                        |    1 +
 data/theme/Adwaita.css                  |    6 +
 data/todo.gresource.xml                 |    1 +
 data/ui/list-selector-list-item.ui      |  101 +++++++
 data/ui/list-selector-panel.ui          |    3 +-
 src/Makefile.am                         |    6 +
 src/views/gtd-list-selector-grid-item.c |  150 +++++------
 src/views/gtd-list-selector-grid-item.h |    7 -
 src/views/gtd-list-selector-grid.c      |   33 +--
 src/views/gtd-list-selector-item.c      |   93 +++++++
 src/views/gtd-list-selector-item.h      |   54 ++++
 src/views/gtd-list-selector-list-item.c |  394 ++++++++++++++++++++++++++
 src/views/gtd-list-selector-list-item.h |   36 +++
 src/views/gtd-list-selector-list.c      |  461 +++++++++++++++++++++++++++++++
 src/views/gtd-list-selector-list.h      |   34 +++
 src/views/gtd-list-selector-panel.c     |  100 +++++--
 16 files changed, 1347 insertions(+), 133 deletions(-)
---
diff --git a/data/Makefile.am b/data/Makefile.am
index 67b1fda..3447944 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -33,6 +33,7 @@ EXTRA_DIST=                     \
   ui/edit-pane.ui \
   ui/initial-setup.ui \
   ui/list-selector-grid-item.ui \
+  ui/list-selector-list-item.ui \
   ui/list-selector-panel.ui \
   ui/list-view.ui \
   ui/notification.ui \
diff --git a/data/theme/Adwaita.css b/data/theme/Adwaita.css
index d646c5e..9dd974f 100644
--- a/data/theme/Adwaita.css
+++ b/data/theme/Adwaita.css
@@ -6,6 +6,12 @@ task-row {
     border-bottom: 1px solid alpha(@borders, 0.4);
 }
 
+row.thumbnail {
+    border: solid 1px @borders;
+    box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.5);
+    border-radius: 16px;
+}
+
 grid-item.thumbnail {
     margin-left: 10px;
     margin-right: 10px;
diff --git a/data/todo.gresource.xml b/data/todo.gresource.xml
index 621312e..d41a50c 100644
--- a/data/todo.gresource.xml
+++ b/data/todo.gresource.xml
@@ -5,6 +5,7 @@
     <file compressed="true" preprocess="xml-stripblanks">ui/edit-pane.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">ui/initial-setup.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">ui/list-selector-grid-item.ui</file>
+    <file compressed="true" preprocess="xml-stripblanks">ui/list-selector-list-item.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">ui/list-selector-panel.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">ui/list-view.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">ui/notification.ui</file>
diff --git a/data/ui/list-selector-list-item.ui b/data/ui/list-selector-list-item.ui
new file mode 100644
index 0000000..7445914
--- /dev/null
+++ b/data/ui/list-selector-list-item.ui
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="3.16"/>
+  <template class="GtdListSelectorListItem" parent="GtkListBoxRow">
+    <property name="visible">True</property>
+    <property name="can_focus">True</property>
+    <child>
+      <object class="GtkBox" id="box">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="margin_start">18</property>
+        <property name="margin_end">18</property>
+        <property name="margin_top">6</property>
+        <property name="margin_bottom">6</property>
+        <property name="spacing">12</property>
+        <child>
+          <object class="GtkEventBox" id="eventbox">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <signal name="enter-notify-event" handler="enter_notify_event" object="GtdListSelectorListItem" 
swapped="no" />
+            <signal name="leave-notify-event" handler="leave_notify_event" object="GtdListSelectorListItem" 
swapped="no" />
+            <child>
+              <object class="GtkStack" id="stack">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="transition_type">crossfade</property>
+                <child>
+                  <object class="GtkImage" id="thumbnail_image">
+                    <property name="width_request">32</property>
+                    <property name="height_request">32</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                  </object>
+                  <packing>
+                    <property name="name">icon</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkCheckButton" id="selection_check">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="halign">center</property>
+                    <property name="valign">center</property>
+                    <property name="draw_indicator">True</property>
+                  </object>
+                  <packing>
+                    <property name="name">checkbox</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel" id="name_label">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="hexpand">True</property>
+            <property name="xalign">0</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel" id="provider_label">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <style>
+              <class name="dim-label"/>
+            </style>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">2</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkSpinner" id="spinner">
+            <property name="can_focus">False</property>
+            <property name="active">True</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">3</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/data/ui/list-selector-panel.ui b/data/ui/list-selector-panel.ui
index 40198ad..8d42e88 100644
--- a/data/ui/list-selector-panel.ui
+++ b/data/ui/list-selector-panel.ui
@@ -229,11 +229,12 @@
     <property name="can_focus">True</property>
     <property name="receives_default">True</property>
     <property name="halign">end</property>
+    <signal name="clicked" handler="gtd_list_selector_panel_switch_view" object="GtdListSelectorPanel" 
swapped="yes" />
     <child>
       <object class="GtkImage" id="view_button_image">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
-        <property name="icon_name">view-grid-symbolic</property>
+        <property name="icon_name">view-list-symbolic</property>
       </object>
     </child>
   </object>
diff --git a/src/Makefile.am b/src/Makefile.am
index eeeaa58..8cd4ff8 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -47,6 +47,12 @@ gnome_todo_SOURCES = \
        views/gtd-list-selector-grid.h \
        views/gtd-list-selector-grid-item.c \
        views/gtd-list-selector-grid-item.h \
+       views/gtd-list-selector-item.c \
+       views/gtd-list-selector-item.h \
+       views/gtd-list-selector-list.c \
+       views/gtd-list-selector-list.h \
+       views/gtd-list-selector-list-item.c \
+       views/gtd-list-selector-list-item.h \
        views/gtd-list-selector-panel.c \
        views/gtd-list-selector-panel.h \
        gtd-application.c \
diff --git a/src/views/gtd-list-selector-grid-item.c b/src/views/gtd-list-selector-grid-item.c
index 84b283a..4a8bfba 100644
--- a/src/views/gtd-list-selector-grid-item.c
+++ b/src/views/gtd-list-selector-grid-item.c
@@ -20,6 +20,7 @@
 #include "gtd-task.h"
 #include "gtd-task-list.h"
 #include "gtd-list-selector-grid-item.h"
+#include "gtd-list-selector-item.h"
 
 #include <glib/gi18n.h>
 
@@ -37,11 +38,16 @@ struct _GtdListSelectorGridItem
   GtdWindowMode              mode;
 
   /* flags */
-  gint                      selected : 1;
+  gint                      selected;
 
 };
 
-G_DEFINE_TYPE (GtdListSelectorGridItem, gtd_list_selector_grid_item, GTK_TYPE_FLOW_BOX_CHILD)
+static void          gtd_list_selector_item_iface_init           (GtdListSelectorItemInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (GtdListSelectorGridItem, gtd_list_selector_grid_item, GTK_TYPE_FLOW_BOX_CHILD,
+                        0,
+                        G_IMPLEMENT_INTERFACE (GTD_TYPE_LIST_SELECTOR_ITEM,
+                                               gtd_list_selector_item_iface_init))
 
 #define LUMINANCE(c)              (0.299 * c->red + 0.587 * c->green + 0.114 * c->blue)
 
@@ -326,7 +332,7 @@ gtd_list_selector_grid_item__button_press_event_cb (GtkWidget *widget,
         }
       else
         {
-          gtd_list_selector_grid_item_set_selected (GTD_LIST_SELECTOR_GRID_ITEM (user_data), 
!item->selected);
+          gtd_list_selector_item_set_selected (GTD_LIST_SELECTOR_ITEM (user_data), !item->selected);
         }
 
       return GDK_EVENT_STOP;
@@ -345,6 +351,50 @@ gtd_list_selector_grid_item_state_flags_changed (GtkWidget     *item,
   gtd_list_selector_grid_item__update_thumbnail (GTD_LIST_SELECTOR_GRID_ITEM (item));
 }
 
+static GtdTaskList*
+gtd_list_selector_grid_item_get_list (GtdListSelectorItem *item)
+{
+  g_return_val_if_fail (GTD_IS_LIST_SELECTOR_GRID_ITEM (item), NULL);
+
+  return GTD_LIST_SELECTOR_GRID_ITEM (item)->list;
+}
+
+static gboolean
+gtd_list_selector_grid_item_get_selected (GtdListSelectorItem *item)
+{
+  g_return_val_if_fail (GTD_IS_LIST_SELECTOR_GRID_ITEM (item), FALSE);
+
+  return GTD_LIST_SELECTOR_GRID_ITEM (item)->selected;
+}
+
+static void
+gtd_list_selector_grid_item_set_selected (GtdListSelectorItem *item,
+                                          gboolean             selected)
+{
+  GtdListSelectorGridItem *self;
+
+  g_return_if_fail (GTD_IS_LIST_SELECTOR_GRID_ITEM (item));
+
+  self = GTD_LIST_SELECTOR_GRID_ITEM (item);
+
+  if (self->selected != selected)
+    {
+      self->selected = selected;
+
+      gtd_list_selector_grid_item__update_thumbnail (self);
+
+      g_object_notify (G_OBJECT (item), "selected");
+    }
+}
+
+static void
+gtd_list_selector_item_iface_init (GtdListSelectorItemInterface *iface)
+{
+  iface->get_list = gtd_list_selector_grid_item_get_list;
+  iface->get_selected = gtd_list_selector_grid_item_get_selected;
+  iface->set_selected = gtd_list_selector_grid_item_set_selected;
+}
+
 static void
 gtd_list_selector_grid_item_finalize (GObject *object)
 {
@@ -395,7 +445,8 @@ gtd_list_selector_grid_item_set_property (GObject      *object,
       break;
 
     case PROP_SELECTED:
-      gtd_list_selector_grid_item_set_selected (self, g_value_get_boolean (value));
+      gtd_list_selector_item_set_selected (GTD_LIST_SELECTOR_ITEM (self),
+                                           g_value_get_boolean (value));
       break;
 
     case PROP_TASK_LIST:
@@ -468,43 +519,27 @@ gtd_list_selector_grid_item_class_init (GtdListSelectorGridItemClass *klass)
    *
    * The parent source of the list.
    */
-  g_object_class_install_property (
-        object_class,
-        PROP_MODE,
-        g_param_spec_enum ("mode",
-                           "Mode of this item",
-                           "The mode of this item, inherited from the parent's mode",
-                           GTD_TYPE_WINDOW_MODE,
-                           GTD_WINDOW_MODE_NORMAL,
-                           G_PARAM_READWRITE));
+  g_object_class_override_property (object_class,
+                                    PROP_MODE,
+                                    "mode");
 
   /**
    * GtdListSelectorGridItem::selected:
    *
    * Whether this item is selected when in %GTD_WINDOW_MODE_SELECTION.
    */
-  g_object_class_install_property (
-        object_class,
-        PROP_SELECTED,
-        g_param_spec_boolean ("selected",
-                              "Whether the task list is selected",
-                              "Whether the task list is selected when in selection mode",
-                              FALSE,
-                              G_PARAM_READWRITE));
+  g_object_class_override_property (object_class,
+                                    PROP_SELECTED,
+                                    "selected");
 
   /**
-   * GtdListSelectorGridItem::task-list:
+   * GtdListSelectorGridItem::list:
    *
    * The parent source of the list.
    */
-  g_object_class_install_property (
-        object_class,
-        PROP_TASK_LIST,
-        g_param_spec_object ("task-list",
-                             "Task list of the item",
-                             "The task list associated with this item",
-                             GTD_TYPE_TASK_LIST,
-                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+  g_object_class_override_property (object_class,
+                                    PROP_TASK_LIST,
+                                    "task-list");
 
   /* template class */
   gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/todo/ui/list-selector-grid-item.ui");
@@ -524,58 +559,3 @@ gtd_list_selector_grid_item_init (GtdListSelectorGridItem *self)
 {
   gtk_widget_init_template (GTK_WIDGET (self));
 }
-
-/**
- * gtd_list_selector_grid_item_get_list:
- * @item: a #GtdListSelectorGridItem
- *
- * Retrieves the internal #GtdTaskList from @item.
- *
- * Returns: (transfer none): the internal #GtdTaskList from @item
- */
-GtdTaskList*
-gtd_list_selector_grid_item_get_list (GtdListSelectorGridItem *item)
-{
-  g_return_val_if_fail (GTD_IS_LIST_SELECTOR_GRID_ITEM (item), NULL);
-
-  return item->list;
-}
-
-/**
- * gtd_list_selector_grid_item_get_selected:
- * @item: a #GtdListSelectorGridItem
- *
- * Retrieves whether @item is selected or not.
- *
- * Returns: %TRUE if @item is selected, %FALSE otherwise
- */
-gboolean
-gtd_list_selector_grid_item_get_selected (GtdListSelectorGridItem *item)
-{
-  g_return_val_if_fail (GTD_IS_LIST_SELECTOR_GRID_ITEM (item), FALSE);
-
-  return item->selected;
-}
-
-/**
- * gtd_list_selector_grid_item_set_selected:
- * @item: a #GtdListSelectorGridItem
- * @selected: %TRUE if @item is selected, %FALSE otherwise
- *
- * Sets whether @item is selected or not.
- */
-void
-gtd_list_selector_grid_item_set_selected (GtdListSelectorGridItem *item,
-                                          gboolean                 selected)
-{
-  g_return_if_fail (GTD_IS_LIST_SELECTOR_GRID_ITEM (item));
-
-  if (item->selected != selected)
-    {
-      item->selected = selected;
-
-      gtd_list_selector_grid_item__update_thumbnail (item);
-
-      g_object_notify (G_OBJECT (item), "selected");
-    }
-}
diff --git a/src/views/gtd-list-selector-grid-item.h b/src/views/gtd-list-selector-grid-item.h
index 2dc68c0..aef37d7 100644
--- a/src/views/gtd-list-selector-grid-item.h
+++ b/src/views/gtd-list-selector-grid-item.h
@@ -31,13 +31,6 @@ G_DECLARE_FINAL_TYPE (GtdListSelectorGridItem, gtd_list_selector_grid_item, GTD,
 
 GtkWidget*              gtd_list_selector_grid_item_new          (GtdTaskList             *list);
 
-GtdTaskList*            gtd_list_selector_grid_item_get_list     (GtdListSelectorGridItem *item);
-
-gboolean                gtd_list_selector_grid_item_get_selected (GtdListSelectorGridItem *item);
-
-void                    gtd_list_selector_grid_item_set_selected (GtdListSelectorGridItem *item,
-                                                                 gboolean                  selected);
-
 G_END_DECLS
 
 #endif /* GTD_LIST_SELECTOR_GRID_ITEM_H */
diff --git a/src/views/gtd-list-selector-grid.c b/src/views/gtd-list-selector-grid.c
index 1eb1edf..e19cfd0 100644
--- a/src/views/gtd-list-selector-grid.c
+++ b/src/views/gtd-list-selector-grid.c
@@ -20,6 +20,7 @@
 #include "gtd-list-selector.h"
 #include "gtd-list-selector-grid.h"
 #include "gtd-list-selector-grid-item.h"
+#include "gtd-list-selector-item.h"
 #include "gtd-manager.h"
 #include "gtd-task-list.h"
 
@@ -80,18 +81,17 @@ gtd_list_selector_grid_list_removed (GtdManager          *manager,
 
   for (l = children; l != NULL; l = l->next)
     {
-      if (gtd_list_selector_grid_item_get_list (l->data) == list)
+      if (gtd_list_selector_item_get_list (l->data) == list)
         gtk_widget_destroy (l->data);
     }
 
   g_list_free (children);
 }
 
-
 static gint
-gtd_list_selector_grid_sort_func (GtdListSelectorGridItem *a,
-                                  GtdListSelectorGridItem *b,
-                                  GtdListSelectorGrid     *selector)
+gtd_list_selector_grid_sort_func (GtdListSelectorItem *a,
+                                  GtdListSelectorItem *b,
+                                  GtdListSelectorGrid *selector)
 {
   GtdProvider *p1;
   GtdProvider *p2;
@@ -99,10 +99,10 @@ gtd_list_selector_grid_sort_func (GtdListSelectorGridItem *a,
   GtdTaskList *l2;
   gint retval = 0;
 
-  l1 = gtd_list_selector_grid_item_get_list (a);
+  l1 = gtd_list_selector_item_get_list (a);
   p1 = gtd_task_list_get_provider (l1);
 
-  l2 = gtd_list_selector_grid_item_get_list (b);
+  l2 = gtd_list_selector_item_get_list (b);
   p2 = gtd_task_list_get_provider (l2);
 
   retval = g_strcmp0 (gtd_provider_get_description (p1), gtd_provider_get_description (p2));
@@ -114,8 +114,8 @@ gtd_list_selector_grid_sort_func (GtdListSelectorGridItem *a,
 }
 
 static gboolean
-gtd_list_selector_grid_filter_func (GtdListSelectorGridItem *item,
-                                    GtdListSelectorGrid     *selector)
+gtd_list_selector_grid_filter_func (GtdListSelectorItem *item,
+                                    GtdListSelectorGrid *selector)
 {
   GtdTaskList *list;
   gboolean return_value;
@@ -130,7 +130,7 @@ gtd_list_selector_grid_filter_func (GtdListSelectorGridItem *item,
   if (!selector->search_query)
     return TRUE;
 
-  list = gtd_list_selector_grid_item_get_list (item);
+  list = gtd_list_selector_item_get_list (item);
   haystack = NULL;
   search_folded = g_utf8_casefold (selector->search_query, -1);
   list_name_folded = g_utf8_casefold (gtd_task_list_get_name (list), -1);
@@ -183,7 +183,7 @@ gtd_list_selector_grid_set_mode (GtdListSelector *selector,
       if (mode != GTD_WINDOW_MODE_SELECTION)
         {
           gtk_container_foreach (GTK_CONTAINER (self),
-                                 (GtkCallback) gtd_list_selector_grid_item_set_selected,
+                                 (GtkCallback) gtd_list_selector_item_set_selected,
                                  FALSE);
         }
 
@@ -234,7 +234,7 @@ gtd_list_selector_grid_get_selected_lists (GtdListSelector *selector)
 
   for (l = children; l != NULL; l = l->next)
     {
-      if (gtd_list_selector_grid_item_get_selected (l->data))
+      if (gtd_list_selector_item_get_selected (l->data))
         selected = g_list_append (selected, l->data);
     }
 
@@ -313,7 +313,7 @@ static void
 gtd_list_selector_grid_child_activated (GtkFlowBox      *flowbox,
                                         GtkFlowBoxChild *child)
 {
-  GtdListSelectorGridItem *item;
+  GtdListSelectorItem *item;
   GtdListSelectorGrid *self;
 
   self = GTD_LIST_SELECTOR_GRID (flowbox);
@@ -321,16 +321,15 @@ gtd_list_selector_grid_child_activated (GtkFlowBox      *flowbox,
   if (!GTD_IS_LIST_SELECTOR_GRID_ITEM (child))
     return;
 
-  item = GTD_LIST_SELECTOR_GRID_ITEM (child);
+  item = GTD_LIST_SELECTOR_ITEM (child);
 
   /* We only mark the item as selected when we're in selection mode */
   if (self->mode == GTD_WINDOW_MODE_SELECTION)
     {
-      gtd_list_selector_grid_item_set_selected (item,
-                                                !gtd_list_selector_grid_item_get_selected (item));
+      gtd_list_selector_item_set_selected (item, !gtd_list_selector_item_get_selected (item));
     }
 
-  g_signal_emit_by_name (flowbox, "list-selected", gtd_list_selector_grid_item_get_list (item));
+  g_signal_emit_by_name (flowbox, "list-selected", gtd_list_selector_item_get_list (item));
 }
 
 static void
diff --git a/src/views/gtd-list-selector-item.c b/src/views/gtd-list-selector-item.c
new file mode 100644
index 0000000..da36217
--- /dev/null
+++ b/src/views/gtd-list-selector-item.c
@@ -0,0 +1,93 @@
+/* gtd-list-selector-item.c
+ *
+ * Copyright (C) 2016 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * This program 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.
+ *
+ * This program 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 program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gtd-enum-types.h"
+#include "gtd-task-list.h"
+#include "gtd-list-selector-item.h"
+
+G_DEFINE_INTERFACE (GtdListSelectorItem, gtd_list_selector_item, GTK_TYPE_WIDGET)
+
+static void
+gtd_list_selector_item_default_init (GtdListSelectorItemInterface *iface)
+{
+  /**
+   * GtdListSelectorItem::list:
+   *
+   * The #GtdTaskList this item represents.
+   */
+  g_object_interface_install_property (iface,
+                                       g_param_spec_object ("task-list",
+                                                            "List of item",
+                                                            "The list this item represents",
+                                                            GTD_TYPE_TASK_LIST,
+                                                            G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+  /**
+   * GtdListSelectorItem::mode:
+   *
+   * The mode of the parent widget.
+   */
+  g_object_interface_install_property (iface,
+                                       g_param_spec_enum ("mode",
+                                                          "Mode of the parent",
+                                                          "The mode of the parent",
+                                                          GTD_TYPE_WINDOW_MODE,
+                                                          GTD_WINDOW_MODE_NORMAL,
+                                                          G_PARAM_READWRITE));
+
+  /**
+   * GtdListSelectorItem::selected:
+   *
+   * Whether the item is selected or not.
+   */
+  g_object_interface_install_property (iface,
+                                       g_param_spec_boolean ("selected",
+                                                             "Whether the item is selected",
+                                                             "Whether the item is selected or not",
+                                                             FALSE,
+                                                             G_PARAM_READWRITE));
+
+}
+
+GtdTaskList*
+gtd_list_selector_item_get_list (GtdListSelectorItem *item)
+{
+  g_return_val_if_fail (GTD_IS_LIST_SELECTOR_ITEM (item), NULL);
+  g_return_val_if_fail (GTD_LIST_SELECTOR_ITEM_GET_IFACE (item)->get_list, NULL);
+
+  return GTD_LIST_SELECTOR_ITEM_GET_IFACE (item)->get_list (item);
+}
+
+gboolean
+gtd_list_selector_item_get_selected (GtdListSelectorItem *item)
+{
+  g_return_val_if_fail (GTD_IS_LIST_SELECTOR_ITEM (item), FALSE);
+  g_return_val_if_fail (GTD_LIST_SELECTOR_ITEM_GET_IFACE (item)->get_selected, FALSE);
+
+  return GTD_LIST_SELECTOR_ITEM_GET_IFACE (item)->get_selected (item);
+}
+
+void
+gtd_list_selector_item_set_selected (GtdListSelectorItem *item,
+                                     gboolean             selected)
+{
+  g_return_if_fail (GTD_IS_LIST_SELECTOR_ITEM (item));
+  g_return_if_fail (GTD_LIST_SELECTOR_ITEM_GET_IFACE (item)->get_list);
+
+  GTD_LIST_SELECTOR_ITEM_GET_IFACE (item)->set_selected (item, selected);
+}
diff --git a/src/views/gtd-list-selector-item.h b/src/views/gtd-list-selector-item.h
new file mode 100644
index 0000000..60850cb
--- /dev/null
+++ b/src/views/gtd-list-selector-item.h
@@ -0,0 +1,54 @@
+/* gtd-list-selector-item.h
+ *
+ * Copyright (C) 2016 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * This program 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.
+ *
+ * This program 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 program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTD_LIST_SELECTOR_ITEM_H
+#define GTD_LIST_SELECTOR_ITEM_H
+
+#include "gtd-enums.h"
+#include "gtd-types.h"
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GTD_TYPE_LIST_SELECTOR_ITEM (gtd_list_selector_item_get_type ())
+
+G_DECLARE_INTERFACE (GtdListSelectorItem, gtd_list_selector_item, GTD, LIST_SELECTOR_ITEM, GtkWidget)
+
+struct _GtdListSelectorItemInterface
+{
+  GTypeInterface parent;
+
+  GtdTaskList*       (*get_list)                                 (GtdListSelectorItem *item);
+
+  gboolean           (*get_selected)                             (GtdListSelectorItem *item);
+
+  void               (*set_selected)                             (GtdListSelectorItem *item,
+                                                                  gboolean             selected);
+};
+
+GtdTaskList*         gtd_list_selector_item_get_list             (GtdListSelectorItem *item);
+
+gboolean             gtd_list_selector_item_get_selected         (GtdListSelectorItem *item);
+
+void                 gtd_list_selector_item_set_selected         (GtdListSelectorItem *item,
+                                                                  gboolean             selected);
+
+G_END_DECLS
+
+#endif /* GTD_LIST_SELECTOR_ITEM_H */
diff --git a/src/views/gtd-list-selector-list-item.c b/src/views/gtd-list-selector-list-item.c
new file mode 100644
index 0000000..13ab118
--- /dev/null
+++ b/src/views/gtd-list-selector-list-item.c
@@ -0,0 +1,394 @@
+/* gtd-list-selector-list-item.c
+ *
+ * Copyright (C) 2016 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * This program 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.
+ *
+ * This program 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 program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gtd-enum-types.h"
+#include "gtd-task.h"
+#include "gtd-task-list.h"
+#include "gtd-list-selector-item.h"
+#include "gtd-list-selector-list-item.h"
+
+#include <math.h>
+
+struct _GtdListSelectorListItem
+{
+  GtkListBoxRow       parent;
+
+  GtkWidget          *eventbox;
+  GtkWidget          *name_label;
+  GtkWidget          *provider_label;
+  GtkWidget          *selection_check;
+  GtkWidget          *spinner;
+  GtkWidget          *stack;
+  GtkWidget          *thumbnail_image;
+
+  /* data */
+  GtdTaskList        *list;
+  GtdWindowMode       mode;
+};
+
+
+static void          gtd_list_selector_item_iface_init           (GtdListSelectorItemInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (GtdListSelectorListItem, gtd_list_selector_list_item, GTK_TYPE_LIST_BOX_ROW,
+                        0,
+                        G_IMPLEMENT_INTERFACE (GTD_TYPE_LIST_SELECTOR_ITEM,
+                                               gtd_list_selector_item_iface_init))
+
+#define LUMINANCE(c)              (0.299 * c->red + 0.587 * c->green + 0.114 * c->blue)
+
+#define THUMBNAIL_SIZE            24
+
+enum {
+  PROP_0,
+  PROP_MODE,
+  PROP_SELECTED,
+  PROP_TASK_LIST,
+  N_PROPS
+};
+
+static void
+set_hand_cursor (GtkWidget *widget,
+                 gboolean   show_cursor)
+{
+  GdkDisplay *display;
+  GdkCursor *cursor;
+
+  if (!gtk_widget_get_realized (widget))
+    return;
+
+  display = gtk_widget_get_display (widget);
+  cursor = show_cursor ? gdk_cursor_new_from_name (display, "pointer") : NULL;
+
+  gdk_window_set_cursor (gtk_widget_get_window (widget), cursor);
+  gdk_display_flush (display);
+
+  g_clear_object (&cursor);
+}
+
+static gboolean
+enter_notify_event (GtkWidget               *widget,
+                    GdkEvent                *event,
+                    GtdListSelectorListItem *item)
+{
+  /* When in selection mode, ignore this and always show the checkbox */
+  if (item->mode == GTD_WINDOW_MODE_SELECTION)
+    return GDK_EVENT_PROPAGATE;
+
+  set_hand_cursor (widget, TRUE);
+  gtk_stack_set_visible_child (GTK_STACK (item->stack), item->selection_check);
+
+  return GDK_EVENT_PROPAGATE;
+}
+
+static gboolean
+leave_notify_event (GtkWidget               *widget,
+                    GdkEvent                *event,
+                    GtdListSelectorListItem *item)
+{
+  /* When in selection mode, ignore this and always show the checkbox */
+  if (item->mode == GTD_WINDOW_MODE_SELECTION)
+    return GDK_EVENT_PROPAGATE;
+
+  set_hand_cursor (widget, FALSE);
+  gtk_stack_set_visible_child (GTK_STACK (item->stack), item->thumbnail_image);
+
+  return GDK_EVENT_PROPAGATE;
+}
+
+static cairo_surface_t*
+gtd_list_selector_list_item__render_thumbnail (GtdListSelectorListItem *item)
+{
+  GtkStyleContext *context;
+  cairo_surface_t *surface;
+  GtdTaskList *list;
+  GdkRGBA *color;
+  cairo_t *cr;
+  gint width;
+  gint height;
+
+  list = item->list;
+  gtk_widget_get_size_request (item->thumbnail_image,
+                               &width,
+                               &height);
+  context = gtk_widget_get_style_context (GTK_WIDGET (item));
+  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                        width,
+                                        height);
+  cr = cairo_create (surface);
+
+  /* Draw the list's background color */
+  color = gtd_task_list_get_color (list);
+
+  gtk_style_context_save (context);
+  gtk_style_context_add_class (context, "thumbnail");
+
+  gtk_render_background (context,
+                         cr,
+                         4.0,
+                         4.0,
+                         THUMBNAIL_SIZE,
+                         THUMBNAIL_SIZE);
+
+  cairo_set_source_rgba (cr,
+                         color->red,
+                         color->green,
+                         color->blue,
+                         color->alpha);
+
+  cairo_arc (cr,
+             THUMBNAIL_SIZE / 2.0 + 4.0,
+             THUMBNAIL_SIZE / 2.0 + 4.0,
+             THUMBNAIL_SIZE / 2.0,
+             0.,
+             2 * M_PI);
+
+  cairo_fill (cr);
+
+  gtk_style_context_restore (context);
+  gdk_rgba_free (color);
+  cairo_destroy (cr);
+
+  return surface;
+}
+
+static void
+gtd_list_selector_list_item__update_thumbnail (GtdListSelectorListItem *item)
+{
+  cairo_surface_t *surface;
+
+  surface = gtd_list_selector_list_item__render_thumbnail (item);
+
+  gtk_image_set_from_surface (GTK_IMAGE (item->thumbnail_image), surface);
+
+  cairo_surface_destroy (surface);
+}
+
+static GtdTaskList*
+gtd_list_selector_list_item_get_list (GtdListSelectorItem *item)
+{
+  g_return_val_if_fail (GTD_IS_LIST_SELECTOR_LIST_ITEM (item), NULL);
+
+  return GTD_LIST_SELECTOR_LIST_ITEM (item)->list;
+}
+
+static gboolean
+gtd_list_selector_list_item_get_selected (GtdListSelectorItem *item)
+{
+  GtdListSelectorListItem *self;
+
+  g_return_val_if_fail (GTD_IS_LIST_SELECTOR_LIST_ITEM (item), FALSE);
+
+  self = GTD_LIST_SELECTOR_LIST_ITEM (item);
+
+  return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->selection_check));
+}
+
+static void
+gtd_list_selector_list_item_set_selected (GtdListSelectorItem *item,
+                                          gboolean             selected)
+{
+  GtdListSelectorListItem *self;
+
+  g_return_if_fail (GTD_IS_LIST_SELECTOR_LIST_ITEM (item));
+
+  self = GTD_LIST_SELECTOR_LIST_ITEM (item);
+
+  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->selection_check)) != selected)
+    {
+      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->selection_check), selected);
+
+      gtd_list_selector_list_item__update_thumbnail (self);
+
+      g_object_notify (G_OBJECT (item), "selected");
+    }
+}
+
+static void
+gtd_list_selector_item_iface_init (GtdListSelectorItemInterface *iface)
+{
+  iface->get_list = gtd_list_selector_list_item_get_list;
+  iface->get_selected = gtd_list_selector_list_item_get_selected;
+  iface->set_selected = gtd_list_selector_list_item_set_selected;
+}
+
+static void
+gtd_list_selector_list_item_get_property (GObject    *object,
+                                          guint       prop_id,
+                                          GValue     *value,
+                                          GParamSpec *pspec)
+{
+  GtdListSelectorListItem *self = GTD_LIST_SELECTOR_LIST_ITEM (object);
+
+  switch (prop_id)
+    {
+    case PROP_MODE:
+      g_value_set_enum (value, self->mode);
+      break;
+
+    case PROP_SELECTED:
+      g_value_set_boolean (value, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->selection_check)));
+      break;
+
+    case PROP_TASK_LIST:
+      g_value_set_object (value, self->list);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtd_list_selector_list_item_set_property (GObject      *object,
+                                          guint         prop_id,
+                                          const GValue *value,
+                                          GParamSpec   *pspec)
+{
+  GtdListSelectorListItem *self = GTD_LIST_SELECTOR_LIST_ITEM (object);
+
+  switch (prop_id)
+    {
+    case PROP_MODE:
+      self->mode = g_value_get_enum (value);
+
+      /* When we go to selection mode, we always show the selection
+       * checkbox. If we're not in selection mode, we only show it
+       * when the mouse hovers.
+       */
+      if (self->mode == GTD_WINDOW_MODE_SELECTION)
+        gtk_stack_set_visible_child (GTK_STACK (self->stack), self->selection_check);
+      else
+        gtk_stack_set_visible_child (GTK_STACK (self->stack), self->thumbnail_image);
+
+      g_object_notify (object, "mode");
+      break;
+
+    case PROP_SELECTED:
+      gtd_list_selector_item_set_selected (GTD_LIST_SELECTOR_ITEM (self),
+                                           g_value_get_boolean (value));
+      break;
+
+    case PROP_TASK_LIST:
+      self->list = g_value_get_object (value);
+      g_object_bind_property (self->list,
+                              "name",
+                              self->name_label,
+                              "label",
+                              G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
+
+      g_object_bind_property (gtd_task_list_get_provider (self->list),
+                              "description",
+                              self->provider_label,
+                              "label",
+                              G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
+
+      g_object_bind_property (self->list,
+                              "ready",
+                              self->spinner,
+                              "visible",
+                              G_BINDING_DEFAULT | G_BINDING_INVERT_BOOLEAN | G_BINDING_SYNC_CREATE);
+
+      g_object_bind_property (self->list,
+                              "ready",
+                              self->spinner,
+                              "active",
+                              G_BINDING_DEFAULT | G_BINDING_INVERT_BOOLEAN | G_BINDING_SYNC_CREATE);
+
+      g_signal_connect_swapped (self->list,
+                                "notify::color",
+                                G_CALLBACK (gtd_list_selector_list_item__update_thumbnail),
+                                self);
+
+      gtd_list_selector_list_item__update_thumbnail (self);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtd_list_selector_list_item_class_init (GtdListSelectorListItemClass *klass)
+{
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->get_property = gtd_list_selector_list_item_get_property;
+  object_class->set_property = gtd_list_selector_list_item_set_property;
+
+  /**
+   * GtdListSelectorListItem::mode:
+   *
+   * The parent source of the list.
+   */
+  g_object_class_override_property (object_class,
+                                    PROP_MODE,
+                                    "mode");
+
+  /**
+   * GtdListSelectorListItem::selected:
+   *
+   * Whether this item is selected when in %GTD_WINDOW_MODE_SELECTION.
+   */
+  g_object_class_override_property (object_class,
+                                    PROP_SELECTED,
+                                    "selected");
+
+  /**
+   * GtdListSelectorListItem::list:
+   *
+   * The parent source of the list.
+   */
+  g_object_class_override_property (object_class,
+                                    PROP_TASK_LIST,
+                                    "task-list");
+
+  /* template class */
+  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/todo/ui/list-selector-list-item.ui");
+
+  gtk_widget_class_bind_template_child (widget_class, GtdListSelectorListItem, eventbox);
+  gtk_widget_class_bind_template_child (widget_class, GtdListSelectorListItem, name_label);
+  gtk_widget_class_bind_template_child (widget_class, GtdListSelectorListItem, provider_label);
+  gtk_widget_class_bind_template_child (widget_class, GtdListSelectorListItem, spinner);
+  gtk_widget_class_bind_template_child (widget_class, GtdListSelectorListItem, stack);
+  gtk_widget_class_bind_template_child (widget_class, GtdListSelectorListItem, selection_check);
+  gtk_widget_class_bind_template_child (widget_class, GtdListSelectorListItem, thumbnail_image);
+
+  gtk_widget_class_bind_template_callback (widget_class, enter_notify_event);
+  gtk_widget_class_bind_template_callback (widget_class, leave_notify_event);
+}
+
+static void
+gtd_list_selector_list_item_init (GtdListSelectorListItem *self)
+{
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  g_object_bind_property (self->selection_check,
+                          "active",
+                          self,
+                          "selected",
+                          G_BINDING_BIDIRECTIONAL);
+}
+
+GtkWidget*
+gtd_list_selector_list_item_new (GtdTaskList *list)
+{
+  return g_object_new (GTD_TYPE_LIST_SELECTOR_LIST_ITEM,
+                       "task-list", list,
+                       NULL);
+}
diff --git a/src/views/gtd-list-selector-list-item.h b/src/views/gtd-list-selector-list-item.h
new file mode 100644
index 0000000..248a73b
--- /dev/null
+++ b/src/views/gtd-list-selector-list-item.h
@@ -0,0 +1,36 @@
+/* gtd-list-selector-list-item.h
+ *
+ * Copyright (C) 2016 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * This program 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.
+ *
+ * This program 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 program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTD_LIST_SELECTOR_LIST_ITEM_H
+#define GTD_LIST_SELECTOR_LIST_ITEM_H
+
+#include "gtd-types.h"
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GTD_TYPE_LIST_SELECTOR_LIST_ITEM (gtd_list_selector_list_item_get_type())
+
+G_DECLARE_FINAL_TYPE (GtdListSelectorListItem, gtd_list_selector_list_item, GTD, LIST_SELECTOR_LIST_ITEM, 
GtkListBoxRow)
+
+GtkWidget*           gtd_list_selector_list_item_new             (GtdTaskList        *list);
+
+G_END_DECLS
+
+#endif /* GTD_LIST_SELECTOR_LIST_ITEM_H */
diff --git a/src/views/gtd-list-selector-list.c b/src/views/gtd-list-selector-list.c
new file mode 100644
index 0000000..33e1eef
--- /dev/null
+++ b/src/views/gtd-list-selector-list.c
@@ -0,0 +1,461 @@
+/* gtd-list-selector-list.c
+ *
+ * Copyright (C) 2016 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * This program 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.
+ *
+ * This program 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 program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "interfaces/gtd-provider.h"
+#include "gtd-list-selector.h"
+#include "gtd-list-selector-item.h"
+#include "gtd-list-selector-list.h"
+#include "gtd-list-selector-list-item.h"
+#include "gtd-manager.h"
+#include "gtd-task-list.h"
+
+struct _GtdListSelectorList
+{
+  GtkListBox          parent;
+
+  gchar              *search_query;
+
+  GtdWindowMode       mode;
+};
+
+static void          gtd_list_selector_iface_init                (GtdListSelectorInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (GtdListSelectorList, gtd_list_selector_list, GTK_TYPE_LIST_BOX,
+                        0,
+                        G_IMPLEMENT_INTERFACE (GTD_TYPE_LIST_SELECTOR,
+                                               gtd_list_selector_iface_init))
+
+enum {
+  PROP_0,
+  PROP_MODE,
+  PROP_SEARCH_QUERY,
+  N_PROPS
+};
+
+static void
+on_row_selected (GtdListSelectorItem *item,
+                 GParamSpec          *pspec,
+                 GtdListSelectorList *self)
+{
+  if (self->mode == GTD_WINDOW_MODE_SELECTION)
+    {
+      g_signal_emit_by_name (self, "list-selected", gtd_list_selector_item_get_list (item));
+    }
+  else if (gtd_list_selector_item_get_selected (item))
+    {
+      gtd_list_selector_set_mode (GTD_LIST_SELECTOR (self), GTD_WINDOW_MODE_SELECTION);
+      g_signal_emit_by_name (self, "list-selected", gtd_list_selector_item_get_list (item));
+    }
+
+}
+
+static void
+gtd_list_selector_list_list_added (GtdManager          *manager,
+                                   GtdTaskList         *list,
+                                   GtdListSelectorList *selector)
+{
+  GtkWidget *item;
+
+  item = gtd_list_selector_list_item_new (list);
+
+  g_object_bind_property (selector,
+                          "mode",
+                          item,
+                          "mode",
+                          G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+
+  /* Different than the GRID view, on LIST view we have
+   * 2 ways to select an item:
+   *
+   * - Clicking in the row when in selection mode
+   * - Toggling the selection checkbox
+   *
+   * Because of that, we have to also track the ::selected
+   * state of each row, since they may change through the
+   * selection box and we don't get notified via ::row_activated.
+   */
+  g_signal_connect (item,
+                    "notify::selected",
+                    G_CALLBACK (on_row_selected),
+                    selector);
+
+  gtk_widget_show (item);
+
+  gtk_container_add (GTK_CONTAINER (selector), item);
+}
+
+
+static void
+gtd_list_selector_list_list_removed (GtdManager          *manager,
+                                     GtdTaskList         *list,
+                                     GtdListSelectorList *selector)
+{
+  GList *children;
+  GList *l;
+
+  children = gtk_container_get_children (GTK_CONTAINER (selector));
+
+  for (l = children; l != NULL; l = l->next)
+    {
+      if (gtd_list_selector_item_get_list (l->data) == list)
+        gtk_widget_destroy (l->data);
+    }
+
+  g_list_free (children);
+}
+
+static gint
+sort_func (GtdListSelectorItem *a,
+           GtdListSelectorItem *b,
+           GtdListSelectorList *selector)
+{
+  GtdProvider *p1;
+  GtdProvider *p2;
+  GtdTaskList *l1;
+  GtdTaskList *l2;
+  gint retval = 0;
+
+  l1 = gtd_list_selector_item_get_list (a);
+  p1 = gtd_task_list_get_provider (l1);
+
+  l2 = gtd_list_selector_item_get_list (b);
+  p2 = gtd_task_list_get_provider (l2);
+
+  retval = g_strcmp0 (gtd_provider_get_description (p1), gtd_provider_get_description (p2));
+
+  if (retval != 0)
+    return retval;
+
+  return g_strcmp0 (gtd_task_list_get_name (l1), gtd_task_list_get_name (l2));
+}
+
+static gboolean
+filter_func (GtdListSelectorItem *item,
+             GtdListSelectorList *selector)
+{
+  GtdTaskList *list;
+  gboolean return_value;
+  gchar *search_folded;
+  gchar *list_name_folded;
+  gchar *haystack;
+
+  /*
+   * When no search query is set, we obviously don't
+   * filter out anything.
+   */
+  if (!selector->search_query)
+    return TRUE;
+
+  list = gtd_list_selector_item_get_list (item);
+  haystack = NULL;
+  search_folded = g_utf8_casefold (selector->search_query, -1);
+  list_name_folded = g_utf8_casefold (gtd_task_list_get_name (list), -1);
+
+  if (!search_folded || search_folded[0] == '\0')
+    {
+      return_value = TRUE;
+      goto out;
+    }
+
+  haystack = g_strstr_len (list_name_folded,
+                           -1,
+                           search_folded);
+
+  return_value = (haystack != NULL);
+
+out:
+  g_free (search_folded);
+  g_free (list_name_folded);
+
+  return return_value;
+}
+
+static void
+update_header_func (GtkListBoxRow *row,
+                    GtkListBoxRow *before,
+                    gpointer       user_data)
+{
+  if (before)
+    {
+      GtkWidget *separator;
+
+      separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
+      gtk_widget_show (separator);
+
+      gtk_list_box_row_set_header (row, separator);
+    }
+}
+
+/******************************
+ * GtdListSelector iface init *
+ ******************************/
+static GtdWindowMode
+gtd_list_selector_list_get_mode (GtdListSelector *selector)
+{
+  g_return_val_if_fail (GTD_IS_LIST_SELECTOR_LIST (selector), GTD_WINDOW_MODE_NORMAL);
+
+  return GTD_LIST_SELECTOR_LIST (selector)->mode;
+}
+
+static void
+gtd_list_selector_list_set_mode (GtdListSelector *selector,
+                                 GtdWindowMode    mode)
+{
+  GtdListSelectorList *self;
+
+  g_return_if_fail (GTD_IS_LIST_SELECTOR_LIST (selector));
+
+  self = GTD_LIST_SELECTOR_LIST (selector);
+
+  if (self->mode != mode)
+    {
+      self->mode = mode;
+
+      /* Unselect all items when leaving selection mode */
+      if (mode != GTD_WINDOW_MODE_SELECTION)
+        {
+          gtk_container_foreach (GTK_CONTAINER (self),
+                                 (GtkCallback) gtd_list_selector_item_set_selected,
+                                 FALSE);
+        }
+
+
+      g_object_notify (G_OBJECT (self), "mode");
+    }
+}
+
+static const gchar*
+gtd_list_selector_list_get_search_query (GtdListSelector *selector)
+{
+  g_return_val_if_fail (GTD_IS_LIST_SELECTOR_LIST (selector), NULL);
+
+  return GTD_LIST_SELECTOR_LIST (selector)->search_query;
+}
+
+static void
+gtd_list_selector_list_set_search_query (GtdListSelector *selector,
+                                         const gchar     *search_query)
+{
+  GtdListSelectorList *self;
+
+  g_return_if_fail (GTD_IS_LIST_SELECTOR_LIST (selector));
+
+  self = GTD_LIST_SELECTOR_LIST (selector);
+
+  if (g_strcmp0 (self->search_query, search_query) != 0)
+    {
+      g_clear_pointer (&self->search_query, g_free);
+      self->search_query = g_strdup (search_query);
+
+      gtk_flow_box_invalidate_filter (GTK_FLOW_BOX (self));
+
+      g_object_notify (G_OBJECT (self), "search-query");
+    }
+}
+
+static GList*
+gtd_list_selector_list_get_selected_lists (GtdListSelector *selector)
+{
+  GList *selected;
+  GList *children;
+  GList *l;
+
+  /* Retrieve the only selected task list */
+  children = gtk_container_get_children (GTK_CONTAINER (selector));
+  selected = NULL;
+
+  for (l = children; l != NULL; l = l->next)
+    {
+      if (gtd_list_selector_item_get_selected (l->data))
+        selected = g_list_append (selected, l->data);
+    }
+
+  g_list_free (children);
+
+  return selected;
+}
+
+static void
+gtd_list_selector_iface_init (GtdListSelectorInterface *iface)
+{
+  iface->get_mode = gtd_list_selector_list_get_mode;
+  iface->set_mode = gtd_list_selector_list_set_mode;
+  iface->get_search_query = gtd_list_selector_list_get_search_query;
+  iface->set_search_query = gtd_list_selector_list_set_search_query;
+  iface->get_selected_lists = gtd_list_selector_list_get_selected_lists;
+}
+
+static void
+gtd_list_selector_list_finalize (GObject *object)
+{
+  GtdListSelectorList *self = (GtdListSelectorList *)object;
+
+  g_clear_pointer (&self->search_query, g_free);
+
+  G_OBJECT_CLASS (gtd_list_selector_list_parent_class)->finalize (object);
+}
+
+static void
+gtd_list_selector_list_get_property (GObject    *object,
+                                     guint       prop_id,
+                                     GValue     *value,
+                                     GParamSpec *pspec)
+{
+  GtdListSelectorList *self = GTD_LIST_SELECTOR_LIST (object);
+
+  switch (prop_id)
+    {
+    case PROP_MODE:
+      g_value_set_enum (value, self->mode);
+      break;
+
+    case PROP_SEARCH_QUERY:
+      g_value_set_string (value, self->search_query);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtd_list_selector_list_set_property (GObject      *object,
+                                     guint         prop_id,
+                                     const GValue *value,
+                                     GParamSpec   *pspec)
+{
+  GtdListSelector *self = GTD_LIST_SELECTOR (object);
+
+  switch (prop_id)
+    {
+    case PROP_MODE:
+      gtd_list_selector_set_mode (self, g_value_get_enum (value));
+      break;
+
+    case PROP_SEARCH_QUERY:
+      gtd_list_selector_set_search_query (self, g_value_get_string (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtd_list_selector_list_row_activated (GtkListBox    *listbox,
+                                      GtkListBoxRow *row)
+{
+  GtdListSelectorItem *item;
+  GtdListSelectorList *self;
+
+  self = GTD_LIST_SELECTOR_LIST (listbox);
+
+  if (!GTD_IS_LIST_SELECTOR_LIST_ITEM (row))
+    return;
+
+  item = GTD_LIST_SELECTOR_ITEM (row);
+
+  /* We only mark the item as selected when we're in selection mode */
+  if (self->mode == GTD_WINDOW_MODE_SELECTION)
+    gtd_list_selector_item_set_selected (item, !gtd_list_selector_item_get_selected (item));
+
+  g_signal_emit_by_name (listbox, "list-selected", gtd_list_selector_item_get_list (item));
+}
+
+static void
+gtd_list_selector_list_class_init (GtdListSelectorListClass *klass)
+{
+  GtkListBoxClass *listbox_class = GTK_LIST_BOX_CLASS (klass);
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  listbox_class->row_activated = gtd_list_selector_list_row_activated;
+
+  object_class->finalize = gtd_list_selector_list_finalize;
+  object_class->get_property = gtd_list_selector_list_get_property;
+  object_class->set_property = gtd_list_selector_list_set_property;
+
+  g_object_class_override_property (object_class,
+                                    PROP_MODE,
+                                    "mode");
+
+  g_object_class_override_property (object_class,
+                                    PROP_SEARCH_QUERY,
+                                    "search-query");
+}
+
+static void
+gtd_list_selector_list_init (GtdListSelectorList *self)
+{
+  GtdManager *manager;
+  GtkWidget *widget;
+  GList *lists;
+  GList *l;
+
+  manager = gtd_manager_get_default ();
+
+  g_signal_connect (manager,
+                    "list-added",
+                    G_CALLBACK (gtd_list_selector_list_list_added),
+                    self);
+  g_signal_connect (manager,
+                    "list-removed",
+                    G_CALLBACK (gtd_list_selector_list_list_removed),
+                    self);
+
+  /* Add already loaded lists */
+  lists = gtd_manager_get_task_lists (manager);
+
+  for (l = lists; l != NULL; l = l->next)
+    {
+      gtd_list_selector_list_list_added (manager,
+                                         l->data,
+                                         self);
+    }
+
+  g_list_free (lists);
+
+  /* Setup header, filter and sorting functions */
+  gtk_list_box_set_header_func (GTK_LIST_BOX (self),
+                                (GtkListBoxUpdateHeaderFunc) update_header_func,
+                                NULL,
+                                NULL);
+
+  gtk_list_box_set_sort_func (GTK_LIST_BOX (self),
+                              (GtkListBoxSortFunc) sort_func,
+                              NULL,
+                              NULL);
+
+  gtk_list_box_set_filter_func (GTK_LIST_BOX (self),
+                                (GtkListBoxFilterFunc) filter_func,
+                                self,
+                                NULL);
+
+  /* Setup some properties */
+  widget = GTK_WIDGET (self);
+
+  gtk_list_box_set_selection_mode (GTK_LIST_BOX (self), GTK_SELECTION_NONE);
+  gtk_widget_set_hexpand (widget, TRUE);
+  gtk_widget_set_vexpand (widget, TRUE);
+  gtk_widget_show_all (widget);
+}
+
+GtkWidget*
+gtd_list_selector_list_new (void)
+{
+  return g_object_new (GTD_TYPE_LIST_SELECTOR_LIST, NULL);
+}
diff --git a/src/views/gtd-list-selector-list.h b/src/views/gtd-list-selector-list.h
new file mode 100644
index 0000000..5eb50a1
--- /dev/null
+++ b/src/views/gtd-list-selector-list.h
@@ -0,0 +1,34 @@
+/* gtd-list-selector-list.h
+ *
+ * Copyright (C) 2016 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * This program 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.
+ *
+ * This program 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 program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTD_LIST_SELECTOR_LIST_H
+#define GTD_LIST_SELECTOR_LIST_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GTD_TYPE_LIST_SELECTOR_LIST (gtd_list_selector_list_get_type())
+
+G_DECLARE_FINAL_TYPE (GtdListSelectorList, gtd_list_selector_list, GTD, LIST_SELECTOR_LIST, GtkListBox)
+
+GtkWidget*           gtd_list_selector_list_new                  (void);
+
+G_END_DECLS
+
+#endif /* GTD_LIST_SELECTOR_LIST_H */
diff --git a/src/views/gtd-list-selector-panel.c b/src/views/gtd-list-selector-panel.c
index bd27417..4e80d56 100644
--- a/src/views/gtd-list-selector-panel.c
+++ b/src/views/gtd-list-selector-panel.c
@@ -21,7 +21,8 @@
 #include "gtd-enum-types.h"
 #include "gtd-list-selector.h"
 #include "gtd-list-selector-grid.h"
-#include "gtd-list-selector-grid-item.h"
+#include "gtd-list-selector-item.h"
+#include "gtd-list-selector-list.h"
 #include "gtd-list-selector-panel.h"
 #include "gtd-manager.h"
 #include "gtd-task-list.h"
@@ -38,8 +39,11 @@ struct _GtdListSelectorPanel
   GtkWidget          *tasklist_view;
 
   GtkWidget          *grid_selector;
+  GtkWidget          *list_selector;
   GMenu              *menu;
 
+  GtdListSelector    *active_selector;
+
   /* Action bar widgets */
   GtkWidget          *actionbar;
   GtkWidget          *delete_button;
@@ -52,6 +56,7 @@ struct _GtdListSelectorPanel
   GtkWidget          *search_button;
   GtkWidget          *selection_button;
   GtkWidget          *view_button;
+  GtkWidget          *view_button_image;
 
   /* Rename widgets */
   GtkWidget          *rename_entry;
@@ -82,6 +87,30 @@ enum {
   N_PROPS
 };
 
+static void
+gtd_list_selector_panel_switch_view (GtdListSelectorPanel *panel)
+{
+  GtkWidget *next_view;
+  const gchar *icon_name;
+
+  if (GTK_WIDGET (panel->active_selector) == panel->grid_selector)
+    {
+      next_view = panel->list_selector;
+      icon_name = "view-grid-symbolic";
+    }
+  else
+    {
+      next_view = panel->grid_selector;
+      icon_name = "view-list-symbolic";
+    }
+
+  gtk_stack_set_visible_child (GTK_STACK (panel->stack), next_view);
+  gtk_image_set_from_icon_name (GTK_IMAGE (panel->view_button_image),
+                                icon_name,
+                                GTK_ICON_SIZE_BUTTON);
+
+  panel->active_selector = GTD_LIST_SELECTOR (next_view);
+}
 
 static void
 gtd_list_selector_panel_select_button_toggled (GtkToggleButton      *button,
@@ -140,7 +169,7 @@ update_action_bar_buttons (GtdListSelectorPanel *panel)
   gboolean all_lists_removable;
   gint selected_lists;
 
-  selection = gtd_list_selector_get_selected_lists (GTD_LIST_SELECTOR (panel->grid_selector));
+  selection = gtd_list_selector_get_selected_lists (panel->active_selector);
   selected_lists = g_list_length (selection);
   all_lists_removable = TRUE;
 
@@ -148,7 +177,7 @@ update_action_bar_buttons (GtdListSelectorPanel *panel)
     {
       GtdTaskList *list;
 
-      list = gtd_list_selector_grid_item_get_list (l->data);
+      list = gtd_list_selector_item_get_list (l->data);
 
       if (!gtd_task_list_is_removable (list))
         {
@@ -255,16 +284,16 @@ gtd_list_selector_panel_rename_task_list (GtdListSelectorPanel *panel)
   if (!gtk_widget_get_sensitive (panel->save_rename_button))
     return;
 
-  selection = gtd_list_selector_get_selected_lists (GTD_LIST_SELECTOR (panel->grid_selector));
+  selection = gtd_list_selector_get_selected_lists (panel->active_selector);
 
   if (selection && selection->data)
     {
-      GtdListSelectorGridItem *item;
+      GtdListSelectorItem *item;
       GtdTaskList *list;
       GtdWindow *window;
 
       item = selection->data;
-      list = gtd_list_selector_grid_item_get_list (item);
+      list = gtd_list_selector_item_get_list (item);
       window = GTD_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (panel)));
 
       gtd_task_list_set_name (list, gtk_entry_get_text (GTK_ENTRY (panel->rename_entry)));
@@ -281,15 +310,15 @@ gtd_list_selector_panel_rename_button_clicked (GtdListSelectorPanel *panel)
 {
   GList *selection;
 
-  selection = gtd_list_selector_get_selected_lists (GTD_LIST_SELECTOR (panel->grid_selector));
+  selection = gtd_list_selector_get_selected_lists (panel->active_selector);
 
   if (selection && selection->data)
     {
-      GtdListSelectorGridItem *item;
+      GtdListSelectorItem *item;
       GtdTaskList *list;
 
       item = selection->data;
-      list = gtd_list_selector_grid_item_get_list (item);
+      list = gtd_list_selector_item_get_list (item);
 
       gtk_popover_set_relative_to (GTK_POPOVER (panel->rename_popover), GTK_WIDGET (item));
       gtk_entry_set_text (GTK_ENTRY (panel->rename_entry), gtd_task_list_get_name (list));
@@ -343,13 +372,13 @@ gtd_list_selector_panel_delete_button_clicked (GtdListSelectorPanel *panel)
   /* Remove selected lists */
   if (response == GTK_RESPONSE_ACCEPT)
     {
-      children = gtd_list_selector_get_selected_lists (GTD_LIST_SELECTOR (panel->grid_selector));
+      children = gtd_list_selector_get_selected_lists (panel->active_selector);
 
       for (l = children; l != NULL; l = l->next)
         {
           GtdTaskList *list;
 
-          list = gtd_list_selector_grid_item_get_list (l->data);
+          list = gtd_list_selector_item_get_list (l->data);
 
           if (gtd_task_list_is_removable (list))
             gtd_manager_remove_task_list (gtd_manager_get_default (), list);
@@ -375,7 +404,7 @@ gtd_list_selector_panel_get_header_widgets (GtdPanel *panel)
 
   widgets = g_list_append (NULL, self->search_button);
   widgets = g_list_append (widgets, self->selection_button);
-  //widgets = g_list_append (widgets, self->view_button);
+  widgets = g_list_append (widgets, self->view_button);
   widgets = g_list_append (widgets, self->back_button);
   widgets = g_list_append (widgets, self->new_list_button);
 
@@ -549,6 +578,7 @@ gtd_list_selector_panel_class_init (GtdListSelectorPanelClass *klass)
   gtk_widget_class_bind_template_child (widget_class, GtdListSelectorPanel, stack);
   gtk_widget_class_bind_template_child (widget_class, GtdListSelectorPanel, tasklist_view);
   gtk_widget_class_bind_template_child (widget_class, GtdListSelectorPanel, view_button);
+  gtk_widget_class_bind_template_child (widget_class, GtdListSelectorPanel, view_button_image);
 
   gtk_widget_class_bind_template_callback (widget_class, gtd_list_selector_panel_back_button_clicked);
   gtk_widget_class_bind_template_callback (widget_class, gtd_list_selector_panel_delete_button_clicked);
@@ -558,37 +588,61 @@ gtd_list_selector_panel_class_init (GtdListSelectorPanelClass *klass)
   gtk_widget_class_bind_template_callback (widget_class, gtd_list_selector_panel_rename_entry_text_changed);
   gtk_widget_class_bind_template_callback (widget_class, gtd_list_selector_panel_rename_task_list);
   gtk_widget_class_bind_template_callback (widget_class, gtd_list_selector_panel_select_button_toggled);
+  gtk_widget_class_bind_template_callback (widget_class, gtd_list_selector_panel_switch_view);
 }
 
 static void
-gtd_list_selector_panel_init (GtdListSelectorPanel *self)
+setup_panel (GtdListSelectorPanel *self,
+             GtkWidget            *selector,
+             const gchar          *stack_name,
+             const gchar          *stack_title)
 {
-  gtk_widget_init_template (GTK_WIDGET (self));
-
-  /* Grid selector */
-  self->grid_selector = gtd_list_selector_grid_new ();
 
   g_object_bind_property (self,
                           "mode",
-                          self->grid_selector,
+                          selector,
                           "mode",
                           G_BINDING_BIDIRECTIONAL);
 
   g_object_bind_property (self->search_entry,
                           "text",
-                          self->grid_selector,
+                          selector,
                           "search-query",
                           G_BINDING_BIDIRECTIONAL);
 
-  g_signal_connect (self->grid_selector,
+  g_signal_connect (selector,
                     "list-selected",
                     G_CALLBACK (gtd_list_selector_panel_list_selected),
                     self);
 
   gtk_stack_add_titled (GTK_STACK (self->stack),
-                        self->grid_selector,
-                        "grid",
-                        "Grid");
+                        selector,
+                        stack_name,
+                        stack_title);
+}
+
+static void
+gtd_list_selector_panel_init (GtdListSelectorPanel *self)
+{
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  /* Grid selector */
+  self->grid_selector = gtd_list_selector_grid_new ();
+
+  setup_panel (self,
+               self->grid_selector,
+               "grid",
+               "Grid");
+
+  self->active_selector = GTD_LIST_SELECTOR (self->grid_selector);
+
+  /* List selector */
+  self->list_selector = gtd_list_selector_list_new ();
+
+  setup_panel (self,
+               self->list_selector,
+               "list",
+               "List");
 
   /* Menu */
   self->menu = g_menu_new ();


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]