1 /* gtd-sidebar.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 "GtdSidebar"
22
23 #include "gtd-debug.h"
24 #include "gtd-manager.h"
25 #include "gtd-max-size-layout.h"
26 #include "gtd-notification.h"
27 #include "gtd-panel.h"
28 #include "gtd-provider.h"
29 #include "gtd-sidebar.h"
30 #include "gtd-sidebar-list-row.h"
31 #include "gtd-sidebar-panel-row.h"
32 #include "gtd-sidebar-provider-row.h"
33 #include "gtd-task-list.h"
34 #include "gtd-task-list-panel.h"
35 #include "gtd-utils.h"
36
37 #include <glib/gi18n.h>
38
39 struct _GtdSidebar
40 {
41 GtdWidget parent;
42
43 GtkListBox *archive_listbox;
44 GtkListBoxRow *archive_row;
45 GtkListBox *listbox;
46 GtkStack *stack;
47
48 GtkStack *panel_stack;
49 GtdPanel *task_list_panel;
50
51 GSimpleActionGroup *action_group;
52 };
53
G_DEFINE_TYPE(GtdSidebar,gtd_sidebar,GTD_TYPE_WIDGET)54 G_DEFINE_TYPE (GtdSidebar, gtd_sidebar, GTD_TYPE_WIDGET)
55
56
57 /*
58 * Auxiliary methods
59 */
60
61 static gboolean
62 activate_row_below (GtdSidebar *self,
63 GtdSidebarListRow *current_row)
64 {
65 GtkWidget *next_row;
66 GtkWidget *parent;
67 GtkWidget *child;
68 gboolean after_deleted;
69
70 parent = gtk_widget_get_parent (GTK_WIDGET (current_row));
71 after_deleted = FALSE;
72 next_row = NULL;
73
74 for (child = gtk_widget_get_first_child (parent);
75 child;
76 child = gtk_widget_get_next_sibling (child))
77 {
78 if (child == (GtkWidget*) current_row)
79 {
80 after_deleted = TRUE;
81 continue;
82 }
83
84 if (!gtk_widget_get_visible (child) ||
85 !gtk_list_box_row_get_activatable (GTK_LIST_BOX_ROW (child)))
86 {
87 continue;
88 }
89
90 next_row = child;
91
92 if (after_deleted)
93 break;
94 }
95
96 if (next_row)
97 g_signal_emit_by_name (next_row, "activate");
98
99 return next_row != NULL;
100 }
101
102 static void
add_task_list(GtdSidebar * self,GtdTaskList * list)103 add_task_list (GtdSidebar *self,
104 GtdTaskList *list)
105 {
106 if (gtd_task_list_is_inbox (list))
107 return;
108
109 g_debug ("Adding task list '%s'", gtd_task_list_get_name (list));
110
111 if (!gtd_task_list_get_archived (list))
112 {
113 gtk_list_box_prepend (self->listbox, gtd_sidebar_list_row_new (list));
114 gtk_list_box_invalidate_filter (self->listbox);
115 }
116 else
117 {
118 gtk_list_box_prepend (self->archive_listbox, gtd_sidebar_list_row_new (list));
119 gtk_list_box_invalidate_filter (self->archive_listbox);
120 }
121 }
122
123 static void
add_panel(GtdSidebar * self,GtdPanel * panel)124 add_panel (GtdSidebar *self,
125 GtdPanel *panel)
126 {
127 GtkWidget *row;
128
129 g_debug ("Adding panel '%s'", gtd_panel_get_panel_name (panel));
130
131 row = gtd_sidebar_panel_row_new (panel);
132
133 gtk_list_box_prepend (self->listbox, row);
134 }
135
136 static void
add_provider(GtdSidebar * self,GtdProvider * provider)137 add_provider (GtdSidebar *self,
138 GtdProvider *provider)
139 {
140 g_debug ("Adding provider '%s'", gtd_provider_get_name (provider));
141
142 gtk_list_box_prepend (self->listbox, gtd_sidebar_provider_row_new (provider));
143 gtk_list_box_prepend (self->archive_listbox, gtd_sidebar_provider_row_new (provider));
144 }
145
146 static gint
compare_panels(GtdSidebarPanelRow * row_a,GtdSidebarPanelRow * row_b)147 compare_panels (GtdSidebarPanelRow *row_a,
148 GtdSidebarPanelRow *row_b)
149 {
150 GtdPanel *panel_a;
151 GtdPanel *panel_b;
152
153 panel_a = gtd_sidebar_panel_row_get_panel (row_a);
154 panel_b = gtd_sidebar_panel_row_get_panel (row_b);
155
156 return gtd_panel_get_priority (panel_b) - gtd_panel_get_priority (panel_a);
157 }
158
159 static gint
compare_providers(GtdSidebarProviderRow * row_a,GtdSidebarProviderRow * row_b)160 compare_providers (GtdSidebarProviderRow *row_a,
161 GtdSidebarProviderRow *row_b)
162 {
163 GtdProvider *provider_a;
164 GtdProvider *provider_b;
165
166 provider_a = gtd_sidebar_provider_row_get_provider (row_a);
167 provider_b = gtd_sidebar_provider_row_get_provider (row_b);
168
169 return gtd_provider_compare (provider_a, provider_b);
170 }
171
172 static gint
compare_lists(GtdSidebarListRow * row_a,GtdSidebarListRow * row_b)173 compare_lists (GtdSidebarListRow *row_a,
174 GtdSidebarListRow *row_b)
175 {
176 GtdTaskList *list_a;
177 GtdTaskList *list_b;
178 gint result;
179
180 list_a = gtd_sidebar_list_row_get_task_list (row_a);
181 list_b = gtd_sidebar_list_row_get_task_list (row_b);
182
183 /* First, compare by their providers */
184 result = gtd_provider_compare (gtd_task_list_get_provider (list_a), gtd_task_list_get_provider (list_b));
185
186 if (result != 0)
187 return result;
188
189 return gtd_collate_compare_strings (gtd_task_list_get_name (list_a), gtd_task_list_get_name (list_b));
190 }
191
192 typedef gpointer (*GetDataFunc) (gpointer data);
193
194 static gpointer
get_row_internal(GtdSidebar * self,GtkListBox * listbox,GType type,GetDataFunc get_data_func,gpointer data)195 get_row_internal (GtdSidebar *self,
196 GtkListBox *listbox,
197 GType type,
198 GetDataFunc get_data_func,
199 gpointer data)
200 {
201 GtkWidget *child;
202
203 for (child = gtk_widget_get_first_child (GTK_WIDGET (listbox));
204 child;
205 child = gtk_widget_get_next_sibling (child))
206 {
207 if (g_type_is_a (G_OBJECT_TYPE (child), type) && get_data_func (child) == data)
208 return child;
209 }
210
211 return NULL;
212 }
213
214 static GtkListBoxRow*
get_row_for_panel(GtdSidebar * self,GtdPanel * panel)215 get_row_for_panel (GtdSidebar *self,
216 GtdPanel *panel)
217 {
218 return get_row_internal (self,
219 self->listbox,
220 GTD_TYPE_SIDEBAR_PANEL_ROW,
221 (GetDataFunc) gtd_sidebar_panel_row_get_panel,
222 panel);
223 }
224
225 static GtkListBoxRow*
get_row_for_provider(GtdSidebar * self,GtkListBox * listbox,GtdProvider * provider)226 get_row_for_provider (GtdSidebar *self,
227 GtkListBox *listbox,
228 GtdProvider *provider)
229 {
230 return get_row_internal (self,
231 listbox,
232 GTD_TYPE_SIDEBAR_PROVIDER_ROW,
233 (GetDataFunc) gtd_sidebar_provider_row_get_provider,
234 provider);
235 }
236
237 static GtkListBoxRow*
get_row_for_task_list(GtdSidebar * self,GtkListBox * listbox,GtdTaskList * list)238 get_row_for_task_list (GtdSidebar *self,
239 GtkListBox *listbox,
240 GtdTaskList *list)
241 {
242 return get_row_internal (self,
243 listbox,
244 GTD_TYPE_SIDEBAR_LIST_ROW,
245 (GetDataFunc) gtd_sidebar_list_row_get_task_list,
246 list);
247 }
248
249 static void
activate_appropriate_row(GtdSidebar * self,GtkListBoxRow * row)250 activate_appropriate_row (GtdSidebar *self,
251 GtkListBoxRow *row)
252 {
253 GtkListBoxRow *to_be_activated;
254
255 if (activate_row_below (self, GTD_SIDEBAR_LIST_ROW (row)))
256 return;
257
258 gtk_widget_activate_action (GTK_WIDGET (self),
259 "task-lists-workspace.toggle-archive",
260 "b",
261 FALSE);
262
263 to_be_activated = gtk_list_box_get_row_at_index (self->listbox, 0);
264 g_signal_emit_by_name (to_be_activated, "activate");
265 }
266
267 /*
268 * Callbacks
269 */
270
271 static void
on_action_move_up_activated_cb(GSimpleAction * simple,GVariant * parameters,gpointer user_data)272 on_action_move_up_activated_cb (GSimpleAction *simple,
273 GVariant *parameters,
274 gpointer user_data)
275 {
276 GtkListBoxRow *selected_row;
277 GtkListBoxRow *previous_row;
278 GtdSidebar *self;
279 gint selected_row_index;
280
281 GTD_ENTRY;
282
283 self = GTD_SIDEBAR (user_data);
284 selected_row = gtk_list_box_get_selected_row (self->listbox);
285 g_assert (selected_row != NULL);
286
287 selected_row_index = gtk_list_box_row_get_index (selected_row);
288 if (selected_row_index == 0)
289 return;
290
291 do
292 {
293 previous_row = gtk_list_box_get_row_at_index (self->listbox,
294 --selected_row_index);
295 }
296 while (previous_row &&
297 (previous_row == self->archive_row ||
298 !gtk_list_box_row_get_activatable (previous_row)));
299
300
301 if (previous_row)
302 g_signal_emit_by_name (previous_row, "activate");
303
304 GTD_EXIT;
305 }
306
307 static void
on_action_move_down_activated_cb(GSimpleAction * simple,GVariant * parameters,gpointer user_data)308 on_action_move_down_activated_cb (GSimpleAction *simple,
309 GVariant *parameters,
310 gpointer user_data)
311 {
312 GtkListBoxRow *selected_row;
313 GtkListBoxRow *next_row;
314 GtdSidebar *self;
315 gint selected_row_index;
316
317 GTD_ENTRY;
318
319 self = GTD_SIDEBAR (user_data);
320 selected_row = gtk_list_box_get_selected_row (self->listbox);
321 g_assert (selected_row != NULL);
322
323 selected_row_index = gtk_list_box_row_get_index (selected_row);
324
325 do
326 {
327 next_row = gtk_list_box_get_row_at_index (self->listbox,
328 ++selected_row_index);
329 }
330 while (next_row &&
331 (next_row == self->archive_row ||
332 !gtk_list_box_row_get_activatable (next_row)));
333
334
335 if (next_row)
336 g_signal_emit_by_name (next_row, "activate");
337
338 GTD_EXIT;
339 }
340
341 static void
on_panel_added_cb(GtdManager * manager,GtdPanel * panel,GtdSidebar * self)342 on_panel_added_cb (GtdManager *manager,
343 GtdPanel *panel,
344 GtdSidebar *self)
345 {
346 add_panel (self, panel);
347 }
348
349 static void
on_panel_removed_cb(GtdManager * manager,GtdPanel * panel,GtdSidebar * self)350 on_panel_removed_cb (GtdManager *manager,
351 GtdPanel *panel,
352 GtdSidebar *self)
353 {
354 GtkListBoxRow *row = get_row_for_panel (self, panel);
355
356 g_debug ("Removing panel '%s'", gtd_panel_get_panel_name (panel));
357
358 if (row)
359 gtk_list_box_remove (self->listbox, GTK_WIDGET (row));
360 }
361
362 static void
on_provider_task_list_removed_cb(GObject * source,GAsyncResult * result,gpointer user_data)363 on_provider_task_list_removed_cb (GObject *source,
364 GAsyncResult *result,
365 gpointer user_data)
366 {
367 g_autoptr (GError) error = NULL;
368
369 gtd_provider_remove_task_list_finish (GTD_PROVIDER (source), result, &error);
370 }
371
372 static void
delete_list_cb(GtdNotification * notification,gpointer user_data)373 delete_list_cb (GtdNotification *notification,
374 gpointer user_data)
375 {
376 GtdTaskList *list;
377 GtdProvider *provider;
378
379 list = GTD_TASK_LIST (user_data);
380 provider = gtd_task_list_get_provider (list);
381
382 g_assert (provider != NULL);
383 g_assert (gtd_task_list_is_removable (list));
384
385 gtd_provider_remove_task_list (provider,
386 list,
387 NULL,
388 on_provider_task_list_removed_cb,
389 NULL);
390 }
391
392 static void
undo_delete_list_cb(GtdNotification * notification,gpointer user_data)393 undo_delete_list_cb (GtdNotification *notification,
394 gpointer user_data)
395 {
396 g_assert (GTD_IS_SIDEBAR_LIST_ROW (user_data));
397
398 gtk_widget_show (GTK_WIDGET (user_data));
399 }
400
401 static void
on_task_list_panel_list_deleted_cb(GtdTaskListPanel * panel,GtdTaskList * list,GtdSidebar * self)402 on_task_list_panel_list_deleted_cb (GtdTaskListPanel *panel,
403 GtdTaskList *list,
404 GtdSidebar *self)
405 {
406 GtdSidebarListRow *row;
407 GtdNotification *notification;
408 g_autofree gchar *title = NULL;
409
410 if (gtd_task_list_get_archived (list))
411 row = (GtdSidebarListRow*) get_row_for_task_list (self, self->archive_listbox, list);
412 else
413 row = (GtdSidebarListRow*) get_row_for_task_list (self, self->listbox, list);
414
415 g_assert (row != NULL && GTD_IS_SIDEBAR_LIST_ROW (row));
416
417 GTD_TRACE_MSG ("Removing task list row from sidebar");
418
419 title = g_strdup_printf (_("Task list <b>%s</b> removed"), gtd_task_list_get_name (list));
420 notification = gtd_notification_new (title, 6000.0);
421 gtd_notification_set_primary_action (notification, delete_list_cb, list);
422 gtd_notification_set_secondary_action (notification, _("Undo"), undo_delete_list_cb, row);
423
424 gtd_manager_send_notification (gtd_manager_get_default (), notification);
425
426 /*
427 * If the deleted list is selected, go to the next one (or previous, if
428 * there are no other task list after this one).
429 */
430 if (gtk_list_box_row_is_selected (GTK_LIST_BOX_ROW (row)))
431 activate_appropriate_row (self, GTK_LIST_BOX_ROW (row));
432
433 gtk_widget_hide (GTK_WIDGET (row));
434 }
435
436 static void
on_listbox_row_activated_cb(GtkListBox * panels_listbox,GtkListBoxRow * row,GtdSidebar * self)437 on_listbox_row_activated_cb (GtkListBox *panels_listbox,
438 GtkListBoxRow *row,
439 GtdSidebar *self)
440 {
441 if (GTD_IS_SIDEBAR_PANEL_ROW (row))
442 {
443 GtdPanel *panel = gtd_sidebar_panel_row_get_panel (GTD_SIDEBAR_PANEL_ROW (row));
444
445 gtk_widget_activate_action (GTK_WIDGET (self),
446 "task-lists-workspace.activate-panel",
447 "(sv)",
448 gtd_panel_get_panel_name (panel),
449 g_variant_new_maybe (G_VARIANT_TYPE_VARIANT, NULL));
450 }
451 else if (GTD_IS_SIDEBAR_PROVIDER_ROW (row))
452 {
453 /* Do nothing */
454 }
455 else if (GTD_IS_SIDEBAR_LIST_ROW (row))
456 {
457 GVariantBuilder builder;
458 GtdProvider *provider;
459 GtdTaskList *list;
460
461 list = gtd_sidebar_list_row_get_task_list (GTD_SIDEBAR_LIST_ROW (row));
462 provider = gtd_task_list_get_provider (list);
463
464 g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
465 g_variant_builder_add (&builder, "{sv}",
466 "provider-id",
467 g_variant_new_string (gtd_provider_get_id (provider)));
468 g_variant_builder_add (&builder, "{sv}",
469 "task-list-id",
470 g_variant_new_string (gtd_object_get_uid (GTD_OBJECT (list))));
471
472 gtk_widget_activate_action (GTK_WIDGET (self),
473 "task-lists-workspace.activate-panel",
474 "(sv)",
475 "task-list-panel",
476 g_variant_builder_end (&builder));
477 }
478 else if (row == self->archive_row)
479 {
480 gtk_widget_activate_action (GTK_WIDGET (self),
481 "task-lists-workspace.toggle-archive",
482 "b",
483 TRUE);
484 }
485 else
486 {
487 g_assert_not_reached ();
488 }
489 }
490
491 static void
on_panel_stack_visible_child_changed_cb(GtkStack * panel_stack,GParamSpec * pspec,GtdSidebar * self)492 on_panel_stack_visible_child_changed_cb (GtkStack *panel_stack,
493 GParamSpec *pspec,
494 GtdSidebar *self)
495 {
496 GtkListBoxRow *panel_row;
497 GtkListBox *listbox;
498 GtdPanel *visible_panel;
499
500 GTD_ENTRY;
501
502 g_assert (GTD_IS_PANEL (gtk_stack_get_visible_child (panel_stack)));
503
504 visible_panel = GTD_PANEL (gtk_stack_get_visible_child (panel_stack));
505 listbox = self->listbox;
506
507 /*
508 * If the currently visible panel is the tasklist panel, we
509 * should choose the tasklist that is visible. Otherwise,
510 * just select the panel.
511 */
512 if (visible_panel == self->task_list_panel)
513 {
514 GtdTaskList *task_list;
515
516 task_list = gtd_task_list_panel_get_task_list (GTD_TASK_LIST_PANEL (self->task_list_panel));
517 g_assert (task_list != NULL);
518
519 panel_row = get_row_for_task_list (self, self->listbox, task_list);
520
521 if (!panel_row)
522 {
523 panel_row = get_row_for_task_list (self, self->archive_listbox, task_list);
524 listbox = self->archive_listbox;
525 }
526 }
527 else
528 {
529 panel_row = get_row_for_panel (self, visible_panel);
530 }
531
532 /* Select the row if it's not already selected*/
533 if (!gtk_list_box_row_is_selected (panel_row))
534 gtk_list_box_select_row (listbox, panel_row);
535
536 GTD_EXIT;
537 }
538
539 static void
on_provider_added_cb(GtdManager * manager,GtdProvider * provider,GtdSidebar * self)540 on_provider_added_cb (GtdManager *manager,
541 GtdProvider *provider,
542 GtdSidebar *self)
543 {
544 add_provider (self, provider);
545 }
546
547 static void
on_provider_removed_cb(GtdManager * manager,GtdProvider * provider,GtdSidebar * self)548 on_provider_removed_cb (GtdManager *manager,
549 GtdProvider *provider,
550 GtdSidebar *self)
551 {
552 GtkListBoxRow *row;
553
554 g_debug ("Removing provider '%s'", gtd_provider_get_name (provider));
555
556 row = get_row_for_provider (self, self->listbox, provider);
557 gtk_list_box_remove (self->listbox, GTK_WIDGET (row));
558
559 row = get_row_for_provider (self, self->archive_listbox, provider);
560 gtk_list_box_remove (self->archive_listbox, GTK_WIDGET (row));
561 }
562
563
564 static void
on_task_list_added_cb(GtdManager * manager,GtdTaskList * list,GtdSidebar * self)565 on_task_list_added_cb (GtdManager *manager,
566 GtdTaskList *list,
567 GtdSidebar *self)
568 {
569 add_task_list (self, list);
570 }
571
572 static void
on_task_list_changed_cb(GtdManager * manager,GtdTaskList * list,GtdSidebar * self)573 on_task_list_changed_cb (GtdManager *manager,
574 GtdTaskList *list,
575 GtdSidebar *self)
576 {
577 GtkListBoxRow *row;
578 GtkListBox *listbox;
579 gboolean archived;
580
581 archived = gtd_task_list_get_archived (list);
582 listbox = archived ? self->archive_listbox : self->listbox;
583 row = get_row_for_task_list (self, listbox, list);
584
585 /*
586 * The task was either archived or unarchived; remove it and add to
587 * the appropriate listbox.
588 */
589 if (!row)
590 {
591 listbox = archived ? self->listbox : self->archive_listbox;
592 row = get_row_for_task_list (self, listbox, list);
593
594 if (!row)
595 goto out;
596
597 /* Change to another panel or taklist */
598 if (gtk_list_box_row_is_selected (row))
599 activate_appropriate_row (self, row);
600
601 /* Destroy the old row */
602 gtk_list_box_remove (listbox, GTK_WIDGET (row));
603
604 /* Add a new row */
605 add_task_list (self, list);
606 }
607
608 out:
609 gtk_list_box_invalidate_filter (listbox);
610 }
611
612 static void
on_task_list_removed_cb(GtdManager * manager,GtdTaskList * list,GtdSidebar * self)613 on_task_list_removed_cb (GtdManager *manager,
614 GtdTaskList *list,
615 GtdSidebar *self)
616 {
617 GtkListBoxRow *row;
618 GtkListBox *listbox;
619
620 g_debug ("Removing task list '%s'", gtd_task_list_get_name (list));
621
622 g_assert (!gtd_task_list_is_inbox (list));
623
624 if (!gtd_task_list_get_archived (list))
625 listbox = self->listbox;
626 else
627 listbox = self->archive_listbox;
628
629 row = get_row_for_task_list (self, listbox, list);
630 if (!row)
631 return;
632
633 gtk_widget_unparent (GTK_WIDGET (row));
634 gtk_list_box_invalidate_filter (listbox);
635 }
636
637 static gboolean
filter_archive_listbox_cb(GtkListBoxRow * row,gpointer user_data)638 filter_archive_listbox_cb (GtkListBoxRow *row,
639 gpointer user_data)
640 {
641 if (GTD_IS_SIDEBAR_LIST_ROW (row))
642 {
643 GtdTaskList *list;
644
645 list = gtd_sidebar_list_row_get_task_list (GTD_SIDEBAR_LIST_ROW (row));
646 return gtd_task_list_get_archived (list);
647 }
648 else if (GTD_IS_SIDEBAR_PROVIDER_ROW (row))
649 {
650 g_autoptr (GList) lists = NULL;
651 GtdProvider *provider;
652 GList *l;
653
654 provider = gtd_sidebar_provider_row_get_provider (GTD_SIDEBAR_PROVIDER_ROW (row));
655 lists = gtd_provider_get_task_lists (provider);
656
657 for (l = lists; l; l = l->next)
658 {
659 if (gtd_task_list_get_archived (l->data))
660 return TRUE;
661 }
662
663 return FALSE;
664 }
665 else
666 {
667 g_assert_not_reached ();
668 }
669
670 return FALSE;
671 }
672
673 static gboolean
filter_listbox_cb(GtkListBoxRow * row,gpointer user_data)674 filter_listbox_cb (GtkListBoxRow *row,
675 gpointer user_data)
676 {
677 GtdTaskList *list;
678
679 if (!GTD_IS_SIDEBAR_LIST_ROW (row))
680 return TRUE;
681
682 list = gtd_sidebar_list_row_get_task_list (GTD_SIDEBAR_LIST_ROW (row));
683 return !gtd_task_list_get_archived (list);
684 }
685
686 static gint
sort_listbox_cb(GtkListBoxRow * row_a,GtkListBoxRow * row_b,gpointer user_data)687 sort_listbox_cb (GtkListBoxRow *row_a,
688 GtkListBoxRow *row_b,
689 gpointer user_data)
690 {
691 GtdSidebar *self = GTD_SIDEBAR (user_data);
692
693 /* Special-case the Archive row */
694 if (row_a == self->archive_row || row_b == self->archive_row)
695 {
696 if (GTD_IS_SIDEBAR_PANEL_ROW (row_b))
697 return 1;
698 else
699 return -1;
700 }
701
702 if (G_OBJECT_TYPE (row_a) != G_OBJECT_TYPE (row_b))
703 {
704 gint result;
705
706 /* Panels go above everything else */
707 if (GTD_IS_SIDEBAR_PANEL_ROW (row_b) != GTD_IS_SIDEBAR_PANEL_ROW (row_a))
708 return GTD_IS_SIDEBAR_PANEL_ROW (row_b) - GTD_IS_SIDEBAR_PANEL_ROW (row_a);
709
710 /*
711 * At this point, we know that row_a and row_b are either provider rows, or
712 * tasklist rows. We also know that they're different, i.e. if row_a is a
713 * provider row, row_b will be a list one, and vice-versa.
714 */
715 if (GTD_IS_SIDEBAR_PROVIDER_ROW (row_a))
716 {
717 GtdProvider *provider_a;
718 GtdTaskList *list_b;
719
720 provider_a = gtd_sidebar_provider_row_get_provider (GTD_SIDEBAR_PROVIDER_ROW (row_a));
721 list_b = gtd_sidebar_list_row_get_task_list (GTD_SIDEBAR_LIST_ROW (row_b));
722
723 /*
724 * If the providers are different, respect the provider order. If the providers are the
725 * same, we must put the provider row above the tasklist row.
726 */
727 result = gtd_provider_compare (provider_a, gtd_task_list_get_provider (list_b));
728
729 if (result != 0)
730 return result;
731
732 return -1;
733 }
734 else
735 {
736 GtdTaskList *list_a;
737 GtdProvider *provider_b;
738
739 list_a = gtd_sidebar_list_row_get_task_list (GTD_SIDEBAR_LIST_ROW (row_a));
740 provider_b = gtd_sidebar_provider_row_get_provider (GTD_SIDEBAR_PROVIDER_ROW (row_b));
741
742 /* See comment above */
743 result = gtd_provider_compare (gtd_task_list_get_provider (list_a), provider_b);
744
745 if (result != 0)
746 return result;
747
748 return 1;
749 }
750 }
751 else
752 {
753 /*
754 * We only reach this section of the code if both rows are of the same type,
755 * so it doesn't matter which one we get the type from.
756 */
757
758 if (GTD_IS_SIDEBAR_PANEL_ROW (row_a))
759 return compare_panels (GTD_SIDEBAR_PANEL_ROW (row_a), GTD_SIDEBAR_PANEL_ROW (row_b));
760
761 if (GTD_IS_SIDEBAR_PROVIDER_ROW (row_a))
762 return compare_providers (GTD_SIDEBAR_PROVIDER_ROW (row_a), GTD_SIDEBAR_PROVIDER_ROW (row_b));
763
764 if (GTD_IS_SIDEBAR_LIST_ROW (row_a))
765 return compare_lists (GTD_SIDEBAR_LIST_ROW (row_a), GTD_SIDEBAR_LIST_ROW (row_b));
766 }
767
768 return 0;
769 }
770
771
772 /*
773 * GObject overrides
774 */
775
776 static void
gtd_sidebar_constructed(GObject * object)777 gtd_sidebar_constructed (GObject *object)
778 {
779 g_autoptr (GList) providers = NULL;
780 GListModel *lists;
781 GtdManager *manager;
782 GtdSidebar *self;
783 GList *l;
784 guint i;
785
786 self = (GtdSidebar *)object;
787 manager = gtd_manager_get_default ();
788
789 G_OBJECT_CLASS (gtd_sidebar_parent_class)->constructed (object);
790
791 /* Add providers */
792 providers = gtd_manager_get_providers (manager);
793
794 for (l = providers; l; l = l->next)
795 add_provider (self, l->data);
796
797 g_signal_connect (manager, "provider-added", G_CALLBACK (on_provider_added_cb), self);
798 g_signal_connect (manager, "provider-removed", G_CALLBACK (on_provider_removed_cb), self);
799
800 /* Add task lists */
801 lists = gtd_manager_get_task_lists_model (manager);
802
803 for (i = 0; i < g_list_model_get_n_items (lists); i++)
804 {
805 g_autoptr (GtdTaskList) list = g_list_model_get_item (lists, i);
806
807 add_task_list (self, list);
808 }
809
810 g_signal_connect (manager, "list-added", G_CALLBACK (on_task_list_added_cb), self);
811 g_signal_connect (manager, "list-changed", G_CALLBACK (on_task_list_changed_cb), self);
812 g_signal_connect (manager, "list-removed", G_CALLBACK (on_task_list_removed_cb), self);
813 }
814
815 static void
gtd_sidebar_class_init(GtdSidebarClass * klass)816 gtd_sidebar_class_init (GtdSidebarClass *klass)
817 {
818 GObjectClass *object_class = G_OBJECT_CLASS (klass);
819 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
820
821 object_class->constructed = gtd_sidebar_constructed;
822
823 g_type_ensure (GTD_TYPE_MAX_SIZE_LAYOUT);
824
825 gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/todo/plugins/task-lists-workspace/gtd-sidebar.ui");
826
827 gtk_widget_class_bind_template_child (widget_class, GtdSidebar, archive_listbox);
828 gtk_widget_class_bind_template_child (widget_class, GtdSidebar, archive_row);
829 gtk_widget_class_bind_template_child (widget_class, GtdSidebar, listbox);
830 gtk_widget_class_bind_template_child (widget_class, GtdSidebar, stack);
831
832 gtk_widget_class_bind_template_callback (widget_class, on_listbox_row_activated_cb);
833
834 gtk_widget_class_set_css_name (widget_class, "sidebar");
835 }
836
837 static void
gtd_sidebar_init(GtdSidebar * self)838 gtd_sidebar_init (GtdSidebar *self)
839 {
840 static const GActionEntry entries[] = {
841 { "move-up", on_action_move_up_activated_cb },
842 { "move-down", on_action_move_down_activated_cb },
843 };
844 gtk_widget_init_template (GTK_WIDGET (self));
845
846 gtk_list_box_set_sort_func (self->listbox, sort_listbox_cb, self, NULL);
847 gtk_list_box_set_filter_func (self->listbox, filter_listbox_cb, self, NULL);
848
849 gtk_list_box_set_sort_func (self->archive_listbox, sort_listbox_cb, self, NULL);
850 gtk_list_box_set_filter_func (self->archive_listbox, filter_archive_listbox_cb, self, NULL);
851
852 self->action_group = g_simple_action_group_new ();
853
854 g_action_map_add_action_entries (G_ACTION_MAP (self->action_group),
855 entries,
856 G_N_ELEMENTS (entries),
857 self);
858
859 gtk_widget_insert_action_group (GTK_WIDGET (self),
860 "sidebar",
861 G_ACTION_GROUP (self->action_group));
862 }
863
864 void
gtd_sidebar_set_panel_stack(GtdSidebar * self,GtkStack * stack)865 gtd_sidebar_set_panel_stack (GtdSidebar *self,
866 GtkStack *stack)
867 {
868 g_return_if_fail (GTD_IS_SIDEBAR (self));
869 g_return_if_fail (GTK_IS_STACK (stack));
870
871 g_assert (self->panel_stack == NULL);
872
873 self->panel_stack = g_object_ref (stack);
874
875 g_signal_connect_object (stack,
876 "notify::visible-child",
877 G_CALLBACK (on_panel_stack_visible_child_changed_cb),
878 self,
879 0);
880 }
881
882
883 void
gtd_sidebar_set_task_list_panel(GtdSidebar * self,GtdPanel * task_list_panel)884 gtd_sidebar_set_task_list_panel (GtdSidebar *self,
885 GtdPanel *task_list_panel)
886 {
887 g_return_if_fail (GTD_IS_SIDEBAR (self));
888 g_return_if_fail (GTD_IS_PANEL (task_list_panel));
889
890 g_assert (self->task_list_panel == NULL);
891
892 self->task_list_panel = g_object_ref (task_list_panel);
893 g_signal_connect_object (self->task_list_panel,
894 "list-deleted",
895 G_CALLBACK (on_task_list_panel_list_deleted_cb),
896 self,
897 0);
898 }
899
900 void
gtd_sidebar_activate(GtdSidebar * self)901 gtd_sidebar_activate (GtdSidebar *self)
902 {
903 GtkListBoxRow *first_row;
904
905 g_assert (GTD_IS_SIDEBAR (self));
906
907 first_row = gtk_list_box_get_row_at_index (self->listbox, 0);
908 g_signal_emit_by_name (first_row, "activate");
909 }
910
911 void
gtd_sidebar_set_archive_visible(GtdSidebar * self,gboolean show_archive)912 gtd_sidebar_set_archive_visible (GtdSidebar *self,
913 gboolean show_archive)
914 {
915 g_assert (GTD_IS_SIDEBAR (self));
916
917 if (show_archive)
918 gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->archive_listbox));
919 else
920 gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->listbox));
921 }
922
923 void
gtd_sidebar_connect(GtdSidebar * self,GtkWidget * workspace)924 gtd_sidebar_connect (GtdSidebar *self,
925 GtkWidget *workspace)
926 {
927 g_signal_connect (workspace, "panel-added", G_CALLBACK (on_panel_added_cb), self);
928 g_signal_connect (workspace, "panel-removed", G_CALLBACK (on_panel_removed_cb), self);
929 }
930