[gnome-todo/wip/gbsneto/plugins: 13/18] views: implement list view
- From: Georges Basile Stavracas Neto <gbsneto src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-todo/wip/gbsneto/plugins: 13/18] views: implement list view
- Date: Mon, 18 Jan 2016 19:05:48 +0000 (UTC)
commit afc5267f1835c2a26d6a3cbfcd49b42dee47e066
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]