1 /* gtd-sidebar-list-row.c
2  *
3  * Copyright 2018-2020 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  *
18  * SPDX-License-Identifier: GPL-3.0-or-later
19  */
20 
21 #define G_LOG_DOMAIN "GtdSidebarListRow"
22 
23 #include "gtd-debug.h"
24 #include "gtd-manager.h"
25 #include "gtd-notification.h"
26 #include "gtd-provider.h"
27 #include "gtd-sidebar-list-row.h"
28 #include "gtd-task.h"
29 #include "gtd-task-list.h"
30 #include "gtd-utils.h"
31 
32 #include <math.h>
33 #include <glib/gi18n.h>
34 
35 struct _GtdSidebarListRow
36 {
37   GtkListBoxRow       parent;
38 
39   GtkImage           *color_icon;
40   GtkLabel           *name_label;
41   GtkLabel           *tasks_counter_label;
42 
43   GtdTaskList        *list;
44 };
45 
46 
47 static void          on_list_changed_cb                          (GtdSidebarListRow  *self);
48 
49 static void          on_list_color_changed_cb                    (GtdTaskList        *list,
50                                                                   GParamSpec         *pspec,
51                                                                   GtdSidebarListRow  *self);
52 
53 G_DEFINE_TYPE (GtdSidebarListRow, gtd_sidebar_list_row, GTK_TYPE_LIST_BOX_ROW)
54 
55 enum
56 {
57   PROP_0,
58   PROP_LIST,
59   N_PROPS
60 };
61 
62 static GParamSpec *properties [N_PROPS];
63 
64 
65 /*
66  * Auxiliary methods
67  */
68 
69 static void
update_color_icon(GtdSidebarListRow * self)70 update_color_icon (GtdSidebarListRow *self)
71 {
72   g_autoptr (GdkPaintable) paintable = NULL;
73   g_autoptr (GdkRGBA) color = NULL;
74 
75   color = gtd_task_list_get_color (self->list);
76   paintable = gtd_create_circular_paintable (color, 12);
77 
78   gtk_image_set_from_paintable (self->color_icon, paintable);
79 }
80 
81 static void
update_counter_label(GtdSidebarListRow * self)82 update_counter_label (GtdSidebarListRow *self)
83 {
84   g_autofree gchar *label = NULL;
85   GListModel *model;
86   guint counter = 0;
87   guint i;
88 
89   model = G_LIST_MODEL (self->list);
90 
91   for (i = 0; i < g_list_model_get_n_items (model); i++)
92     counter += !gtd_task_get_complete (g_list_model_get_item (model, i));
93 
94   label = counter > 0 ? g_strdup_printf ("%u", counter) : g_strdup ("");
95 
96   gtk_label_set_label (self->tasks_counter_label, label);
97 }
98 
99 static void
set_list(GtdSidebarListRow * self,GtdTaskList * list)100 set_list (GtdSidebarListRow *self,
101           GtdTaskList       *list)
102 {
103   g_assert (list != NULL);
104   g_assert (self->list == NULL);
105 
106   self->list = g_object_ref (list);
107 
108   g_object_bind_property (list,
109                           "name",
110                           self->name_label,
111                           "label",
112                           G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
113 
114   g_object_bind_property (gtd_task_list_get_provider (list),
115                           "enabled",
116                           self,
117                           "visible",
118                           G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
119 
120   /* Always keep the counter label updated */
121   g_signal_connect_object (list, "task-added", G_CALLBACK (on_list_changed_cb), self, G_CONNECT_SWAPPED);
122   g_signal_connect_object (list, "task-updated", G_CALLBACK (on_list_changed_cb), self, G_CONNECT_SWAPPED);
123   g_signal_connect_object (list, "task-removed", G_CALLBACK (on_list_changed_cb), self, G_CONNECT_SWAPPED);
124 
125   update_counter_label (self);
126 
127   /* And also the color icon */
128   g_signal_connect_object (list, "notify::color", G_CALLBACK (on_list_color_changed_cb), self, 0);
129 
130   update_color_icon (self);
131 }
132 
133 
134 /*
135  * Callbacks
136  */
137 
138 static void
on_list_changed_cb(GtdSidebarListRow * self)139 on_list_changed_cb (GtdSidebarListRow *self)
140 {
141   update_counter_label (self);
142 }
143 
144 static void
on_list_color_changed_cb(GtdTaskList * list,GParamSpec * pspec,GtdSidebarListRow * self)145 on_list_color_changed_cb (GtdTaskList       *list,
146                           GParamSpec        *pspec,
147                           GtdSidebarListRow *self)
148 {
149   update_color_icon (self);
150 }
151 
152 static void
on_rename_popover_hidden_cb(GtkPopover * popover,GtdSidebarListRow * self)153 on_rename_popover_hidden_cb (GtkPopover        *popover,
154                              GtdSidebarListRow *self)
155 {
156   /*
157    * Remove the relative to, to remove the popover from the widget
158    * list and avoid parsing any CSS for it. It's a small performance
159    * improvement.
160    */
161   gtk_widget_set_parent (GTK_WIDGET (popover), NULL);
162 }
163 
164 
165 /*
166  * GObject overrides
167  */
168 
169 static void
gtd_sidebar_list_row_finalize(GObject * object)170 gtd_sidebar_list_row_finalize (GObject *object)
171 {
172   GtdSidebarListRow *self = (GtdSidebarListRow *)object;
173 
174   g_clear_object (&self->list);
175 
176   G_OBJECT_CLASS (gtd_sidebar_list_row_parent_class)->finalize (object);
177 }
178 
179 static void
gtd_sidebar_list_row_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)180 gtd_sidebar_list_row_get_property (GObject    *object,
181                                    guint       prop_id,
182                                    GValue     *value,
183                                    GParamSpec *pspec)
184 {
185   GtdSidebarListRow *self = GTD_SIDEBAR_LIST_ROW (object);
186 
187   switch (prop_id)
188     {
189     case PROP_LIST:
190       g_value_set_object (value, self->list);
191       break;
192 
193     default:
194       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
195     }
196 }
197 
198 static void
gtd_sidebar_list_row_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)199 gtd_sidebar_list_row_set_property (GObject      *object,
200                                    guint         prop_id,
201                                    const GValue *value,
202                                    GParamSpec   *pspec)
203 {
204   GtdSidebarListRow *self = GTD_SIDEBAR_LIST_ROW (object);
205 
206   switch (prop_id)
207     {
208     case PROP_LIST:
209       set_list (self, g_value_get_object (value));
210       break;
211 
212     default:
213       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
214     }
215 }
216 
217 static void
gtd_sidebar_list_row_class_init(GtdSidebarListRowClass * klass)218 gtd_sidebar_list_row_class_init (GtdSidebarListRowClass *klass)
219 {
220   GObjectClass *object_class = G_OBJECT_CLASS (klass);
221   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
222 
223   object_class->finalize = gtd_sidebar_list_row_finalize;
224   object_class->get_property = gtd_sidebar_list_row_get_property;
225   object_class->set_property = gtd_sidebar_list_row_set_property;
226 
227   properties[PROP_LIST] = g_param_spec_object ("list",
228                                                "List",
229                                                "The task list this row represents",
230                                                GTD_TYPE_TASK_LIST,
231                                                G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
232 
233   g_object_class_install_properties (object_class, N_PROPS, properties);
234 
235   gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/todo/plugins/task-lists-workspace/gtd-sidebar-list-row.ui");
236 
237   gtk_widget_class_bind_template_child (widget_class, GtdSidebarListRow, color_icon);
238   gtk_widget_class_bind_template_child (widget_class, GtdSidebarListRow, name_label);
239   gtk_widget_class_bind_template_child (widget_class, GtdSidebarListRow, tasks_counter_label);
240 
241   gtk_widget_class_bind_template_callback (widget_class, on_rename_popover_hidden_cb);
242 }
243 
244 static void
gtd_sidebar_list_row_init(GtdSidebarListRow * self)245 gtd_sidebar_list_row_init (GtdSidebarListRow *self)
246 {
247   gtk_widget_init_template (GTK_WIDGET (self));
248 }
249 
250 GtkWidget*
gtd_sidebar_list_row_new(GtdTaskList * list)251 gtd_sidebar_list_row_new (GtdTaskList *list)
252 {
253   return g_object_new (GTD_TYPE_SIDEBAR_LIST_ROW,
254                        "list", list,
255                        NULL);
256 }
257 
258 GtdTaskList*
gtd_sidebar_list_row_get_task_list(GtdSidebarListRow * self)259 gtd_sidebar_list_row_get_task_list (GtdSidebarListRow *self)
260 {
261   g_return_val_if_fail (GTD_IS_SIDEBAR_LIST_ROW (self), NULL);
262 
263   return self->list;
264 }
265