1 /* gtd-task-row.c
2 *
3 * Copyright (C) 2015-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
19 #define G_LOG_DOMAIN "GtdTaskRow"
20
21 #include "gtd-debug.h"
22 #include "gtd-edit-pane.h"
23 #include "gtd-manager.h"
24 #include "gtd-markdown-renderer.h"
25 #include "gtd-provider.h"
26 #include "gtd-star-widget.h"
27 #include "gtd-task-row.h"
28 #include "gtd-task.h"
29 #include "gtd-task-list.h"
30 #include "gtd-task-list-view.h"
31 #include "gtd-utils-private.h"
32 #include "gtd-widget.h"
33
34 #include <glib/gi18n.h>
35 #include <gdk/gdk.h>
36 #include <gtk/gtk.h>
37
38 struct _GtdTaskRow
39 {
40 GtdWidget parent;
41
42 /*<private>*/
43 GtkWidget *content_box;
44 GtkWidget *main_box;
45
46 GtkWidget *done_check;
47 GtkWidget *edit_panel_revealer;
48 GtkWidget *header_event_box;
49 GtdStarWidget *star_widget;
50 GtkWidget *title_entry;
51
52 /* task widgets */
53 GtkLabel *task_date_label;
54 GtkLabel *task_list_label;
55
56 /* dnd widgets */
57 GtkWidget *dnd_box;
58 GtkWidget *dnd_icon;
59 gint clicked_x;
60 gint clicked_y;
61
62 /* data */
63 GtdTask *task;
64
65 GtdEditPane *edit_pane;
66
67 GtdMarkdownRenderer *renderer;
68 GPtrArray *bindings;
69
70 gboolean active;
71 gboolean changed;
72 };
73
74 #define PRIORITY_ICON_SIZE 8
75
76 static void on_star_widget_activated_cb (GtdStarWidget *star_widget,
77 GParamSpec *pspec,
78 GtdTaskRow *self);
79
80 G_DEFINE_TYPE (GtdTaskRow, gtd_task_row, GTD_TYPE_WIDGET)
81
82 enum
83 {
84 ENTER,
85 EXIT,
86 REMOVE_TASK,
87 NUM_SIGNALS
88 };
89
90 enum
91 {
92 PROP_0,
93 PROP_RENDERER,
94 PROP_TASK,
95 LAST_PROP
96 };
97
98 static guint signals[NUM_SIGNALS] = { 0, };
99
100
101 /*
102 * Auxiliary methods
103 */
104
105 static gboolean
date_to_label_binding_cb(GBinding * binding,const GValue * from_value,GValue * to_value,gpointer user_data)106 date_to_label_binding_cb (GBinding *binding,
107 const GValue *from_value,
108 GValue *to_value,
109 gpointer user_data)
110 {
111 g_autofree gchar *new_label = NULL;
112 GDateTime *dt;
113
114 g_return_val_if_fail (GTD_IS_TASK_ROW (user_data), FALSE);
115
116 dt = g_value_get_boxed (from_value);
117
118 if (dt)
119 {
120 g_autoptr (GDateTime) today = g_date_time_new_now_local ();
121
122 if (g_date_time_get_year (dt) == g_date_time_get_year (today) &&
123 g_date_time_get_month (dt) == g_date_time_get_month (today))
124 {
125 if (g_date_time_get_day_of_month (dt) == g_date_time_get_day_of_month (today))
126 {
127 new_label = g_strdup (_("Today"));
128 }
129 else if (g_date_time_get_day_of_month (dt) == g_date_time_get_day_of_month (today) + 1)
130 {
131 new_label = g_strdup (_("Tomorrow"));
132 }
133 else if (g_date_time_get_day_of_month (dt) == g_date_time_get_day_of_month (today) - 1)
134 {
135 new_label = g_strdup (_("Yesterday"));
136 }
137 else if (g_date_time_get_day_of_year (dt) > g_date_time_get_day_of_month (today) &&
138 g_date_time_get_day_of_year (dt) < g_date_time_get_day_of_month (today) + 7)
139 {
140 new_label = g_date_time_format (dt, "%A");
141 }
142 else
143 {
144 new_label = g_date_time_format (dt, "%x");
145 }
146 }
147 else
148 {
149 new_label = g_date_time_format (dt, "%x");
150 }
151 }
152 else
153 {
154 new_label = g_strdup ("");
155 }
156
157 g_value_set_string (to_value, new_label);
158
159 return TRUE;
160 }
161
162 static GtkWidget*
create_transient_row(GtdTaskRow * self)163 create_transient_row (GtdTaskRow *self)
164 {
165 GtdTaskRow *new_row;
166
167 new_row = GTD_TASK_ROW (gtd_task_row_new (self->task, self->renderer));
168
169 gtk_widget_set_size_request (GTK_WIDGET (new_row),
170 gtk_widget_get_allocated_width (GTK_WIDGET (self)),
171 -1);
172
173 gtk_revealer_set_reveal_child (GTK_REVEALER (new_row->edit_panel_revealer), self->active);
174
175 gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (new_row)), "background");
176 gtk_widget_set_opacity (GTK_WIDGET (new_row), 0.66);
177
178 return GTK_WIDGET (new_row);
179 }
180
181
182 /*
183 * Callbacks
184 */
185
186 static void
on_task_updated_cb(GObject * object,GAsyncResult * result,gpointer user_data)187 on_task_updated_cb (GObject *object,
188 GAsyncResult *result,
189 gpointer user_data)
190 {
191 g_autoptr (GError) error = NULL;
192
193 gtd_provider_update_task_finish (GTD_PROVIDER (object), result, &error);
194
195 if (error)
196 {
197 g_warning ("Error updating task: %s", error->message);
198 return;
199 }
200 }
201
202 static void
on_remove_task_cb(GtdEditPane * edit_panel,GtdTask * task,GtdTaskRow * self)203 on_remove_task_cb (GtdEditPane *edit_panel,
204 GtdTask *task,
205 GtdTaskRow *self)
206 {
207 g_signal_emit (self, signals[REMOVE_TASK], 0);
208 }
209
210 static void
on_button_press_event_cb(GtkGestureClick * gesture,gint n_press,gdouble x,gdouble y,GtdTaskRow * self)211 on_button_press_event_cb (GtkGestureClick *gesture,
212 gint n_press,
213 gdouble x,
214 gdouble y,
215 GtdTaskRow *self)
216 {
217 GtkWidget *widget;
218 gdouble real_x;
219 gdouble real_y;
220
221 widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
222
223 gtk_widget_translate_coordinates (widget,
224 GTK_WIDGET (self),
225 x,
226 y,
227 &real_x,
228 &real_y);
229
230 self->clicked_x = real_x;
231 self->clicked_y = real_y;
232
233 GTD_TRACE_MSG ("GtkGestureClick:pressed received from a %s at %.1lf,%.1lf (%.1lf,%.1lf)",
234 G_OBJECT_TYPE_NAME (widget),
235 x,
236 y,
237 real_x,
238 real_y);
239 }
240
241 static GdkContentProvider*
on_drag_prepare_cb(GtkDragSource * source,gdouble x,gdouble y,GtdTaskRow * self)242 on_drag_prepare_cb (GtkDragSource *source,
243 gdouble x,
244 gdouble y,
245 GtdTaskRow *self)
246 {
247 GTD_ENTRY;
248 GTD_RETURN (gdk_content_provider_new_typed (GTD_TYPE_TASK, self->task));
249 }
250
251 static void
on_drag_begin_cb(GtkDragSource * source,GdkDrag * drag,GtdTaskRow * self)252 on_drag_begin_cb (GtkDragSource *source,
253 GdkDrag *drag,
254 GtdTaskRow *self)
255 {
256 GtkWidget *drag_icon;
257 GtkWidget *new_row;
258 GtkWidget *widget;
259
260 GTD_ENTRY;
261
262 widget = GTK_WIDGET (self);
263
264 gtk_widget_set_cursor_from_name (widget, "grabbing");
265
266 new_row = create_transient_row (self);
267 drag_icon = gtk_drag_icon_get_for_drag (drag);
268 gtk_drag_icon_set_child (GTK_DRAG_ICON (drag_icon), new_row);
269 gdk_drag_set_hotspot (drag, self->clicked_x, self->clicked_y);
270
271 gtk_widget_hide (widget);
272
273 GTD_EXIT;
274 }
275
276 static gboolean
on_drag_cancelled_cb(GtkDragSource * source,GdkDrag * drag,GdkDragCancelReason result,GtdTaskRow * self)277 on_drag_cancelled_cb (GtkDragSource *source,
278 GdkDrag *drag,
279 GdkDragCancelReason result,
280 GtdTaskRow *self)
281 {
282 GTD_ENTRY;
283
284 gtk_widget_set_cursor_from_name (GTK_WIDGET (self), NULL);
285 gtk_widget_show (GTK_WIDGET (self));
286
287 GTD_RETURN (FALSE);
288 }
289
290 static void
on_complete_check_toggled_cb(GtkCheckButton * button,GtdTaskRow * self)291 on_complete_check_toggled_cb (GtkCheckButton *button,
292 GtdTaskRow *self)
293 {
294 GTD_ENTRY;
295
296 g_assert (GTD_IS_TASK (self->task));
297
298 gtd_task_set_complete (self->task, gtk_check_button_get_active (button));
299 gtd_provider_update_task (gtd_task_get_provider (self->task),
300 self->task,
301 NULL,
302 on_task_updated_cb,
303 self);
304
305 GTD_EXIT;
306 }
307
308 static void
on_complete_changed_cb(GtdTaskRow * self,GParamSpec * pspec,GtdTask * task)309 on_complete_changed_cb (GtdTaskRow *self,
310 GParamSpec *pspec,
311 GtdTask *task)
312 {
313 GtkStyleContext *context;
314 gboolean complete;
315
316 complete = gtd_task_get_complete (task);
317 context = gtk_widget_get_style_context (GTK_WIDGET (self));
318
319 if (complete)
320 gtk_style_context_add_class (context, "complete");
321 else
322 gtk_style_context_remove_class (context, "complete");
323
324 /* Update the toggle button as well */
325 g_signal_handlers_block_by_func (self->done_check, on_complete_check_toggled_cb, self);
326 gtk_check_button_set_active (GTK_CHECK_BUTTON (self->done_check), complete);
327 g_signal_handlers_unblock_by_func (self->done_check, on_complete_check_toggled_cb, self);
328 }
329
330 static void
on_task_important_changed_cb(GtdTask * task,GParamSpec * pspec,GtdTaskRow * self)331 on_task_important_changed_cb (GtdTask *task,
332 GParamSpec *pspec,
333 GtdTaskRow *self)
334 {
335 g_signal_handlers_block_by_func (self->star_widget, on_star_widget_activated_cb, self);
336
337 gtd_star_widget_set_active (self->star_widget, gtd_task_get_important (task));
338
339 g_signal_handlers_unblock_by_func (self->star_widget, on_star_widget_activated_cb, self);
340 }
341
342 static void
on_star_widget_activated_cb(GtdStarWidget * star_widget,GParamSpec * pspec,GtdTaskRow * self)343 on_star_widget_activated_cb (GtdStarWidget *star_widget,
344 GParamSpec *pspec,
345 GtdTaskRow *self)
346 {
347 g_signal_handlers_block_by_func (self->task, on_task_important_changed_cb, self);
348
349 gtd_task_set_important (self->task, gtd_star_widget_get_active (star_widget));
350 gtd_provider_update_task (gtd_task_get_provider (self->task),
351 self->task,
352 NULL,
353 on_task_updated_cb,
354 self);
355
356 g_signal_handlers_unblock_by_func (self->task, on_task_important_changed_cb, self);
357 }
358
359 static gboolean
on_key_pressed_cb(GtkEventControllerKey * controller,guint keyval,guint keycode,GdkModifierType modifiers,GtdTaskRow * self)360 on_key_pressed_cb (GtkEventControllerKey *controller,
361 guint keyval,
362 guint keycode,
363 GdkModifierType modifiers,
364 GtdTaskRow *self)
365 {
366 GTD_ENTRY;
367
368 /* Exit when pressing Esc without modifiers */
369 if (keyval == GDK_KEY_Escape && !(modifiers & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)))
370 {
371 GTD_TRACE_MSG ("Escape pressed, closing task…");
372 gtd_task_row_set_active (self, FALSE);
373 }
374
375 GTD_RETURN (GDK_EVENT_PROPAGATE);
376 }
377
378 static void
on_task_changed_cb(GtdTaskRow * self)379 on_task_changed_cb (GtdTaskRow *self)
380 {
381 g_debug ("Task changed");
382
383 self->changed = TRUE;
384 }
385
386
387 /*
388 * GObject overrides
389 */
390
391 static void
gtd_task_row_finalize(GObject * object)392 gtd_task_row_finalize (GObject *object)
393 {
394 GtdTaskRow *self = GTD_TASK_ROW (object);
395
396 if (self->changed)
397 {
398 if (self->task)
399 {
400 gtd_provider_update_task (gtd_task_get_provider (self->task),
401 self->task,
402 NULL,
403 on_task_updated_cb,
404 self);
405 }
406 self->changed = FALSE;
407 }
408
409 g_clear_object (&self->task);
410
411 G_OBJECT_CLASS (gtd_task_row_parent_class)->finalize (object);
412 }
413
414 static void
gtd_task_row_dispose(GObject * object)415 gtd_task_row_dispose (GObject *object)
416 {
417 GtdTaskRow *self;
418 GtdTask *task;
419
420 self = GTD_TASK_ROW (object);
421 task = self->task;
422
423 g_clear_pointer (&self->bindings, g_ptr_array_unref);
424
425 if (task)
426 g_signal_handlers_disconnect_by_func (task, on_complete_changed_cb, self);
427
428 G_OBJECT_CLASS (gtd_task_row_parent_class)->dispose (object);
429 }
430
431 static void
gtd_task_row_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)432 gtd_task_row_get_property (GObject *object,
433 guint prop_id,
434 GValue *value,
435 GParamSpec *pspec)
436 {
437 GtdTaskRow *self = GTD_TASK_ROW (object);
438
439 switch (prop_id)
440 {
441 case PROP_RENDERER:
442 g_value_set_object (value, self->renderer);
443 break;
444
445 case PROP_TASK:
446 g_value_set_object (value, self->task);
447 break;
448
449 default:
450 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
451 }
452 }
453
454 static void
gtd_task_row_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)455 gtd_task_row_set_property (GObject *object,
456 guint prop_id,
457 const GValue *value,
458 GParamSpec *pspec)
459 {
460 GtdTaskRow *self = GTD_TASK_ROW (object);
461
462 switch (prop_id)
463 {
464 case PROP_RENDERER:
465 self->renderer = g_value_get_object (value);
466 break;
467
468 case PROP_TASK:
469 gtd_task_row_set_task (self, g_value_get_object (value));
470 break;
471
472 default:
473 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
474 }
475 }
476
477 static void
gtd_task_row_class_init(GtdTaskRowClass * klass)478 gtd_task_row_class_init (GtdTaskRowClass *klass)
479 {
480 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
481 GObjectClass *object_class = G_OBJECT_CLASS (klass);
482
483 object_class->dispose = gtd_task_row_dispose;
484 object_class->finalize = gtd_task_row_finalize;
485 object_class->get_property = gtd_task_row_get_property;
486 object_class->set_property = gtd_task_row_set_property;
487
488 /**
489 * GtdTaskRow::renderer:
490 *
491 * The internal markdown renderer.
492 */
493 g_object_class_install_property (
494 object_class,
495 PROP_RENDERER,
496 g_param_spec_object ("renderer",
497 "Renderer",
498 "Renderer",
499 GTD_TYPE_MARKDOWN_RENDERER,
500 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
501
502 /**
503 * GtdTaskRow::task:
504 *
505 * The task that this row represents, or %NULL.
506 */
507 g_object_class_install_property (
508 object_class,
509 PROP_TASK,
510 g_param_spec_object ("task",
511 "Task of the row",
512 "The task that this row represents",
513 GTD_TYPE_TASK,
514 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
515
516 /**
517 * GtdTaskRow::enter:
518 *
519 * Emitted when the row is focused and in the editing state.
520 */
521 signals[ENTER] = g_signal_new ("enter",
522 GTD_TYPE_TASK_ROW,
523 G_SIGNAL_RUN_LAST,
524 0,
525 NULL,
526 NULL,
527 NULL,
528 G_TYPE_NONE,
529 0);
530
531 /**
532 * GtdTaskRow::exit:
533 *
534 * Emitted when the row is unfocused and leaves the editing state.
535 */
536 signals[EXIT] = g_signal_new ("exit",
537 GTD_TYPE_TASK_ROW,
538 G_SIGNAL_RUN_LAST,
539 0,
540 NULL,
541 NULL,
542 NULL,
543 G_TYPE_NONE,
544 0);
545
546 /**
547 * GtdTaskRow::remove-task:
548 *
549 * Emitted when the user wants to delete the task represented by this row.
550 */
551 signals[REMOVE_TASK] = g_signal_new ("remove-task",
552 GTD_TYPE_TASK_ROW,
553 G_SIGNAL_RUN_LAST,
554 0,
555 NULL,
556 NULL,
557 NULL,
558 G_TYPE_NONE,
559 0);
560
561 gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/todo/ui/gtd-task-row.ui");
562
563 gtk_widget_class_bind_template_child (widget_class, GtdTaskRow, content_box);
564 gtk_widget_class_bind_template_child (widget_class, GtdTaskRow, dnd_box);
565 gtk_widget_class_bind_template_child (widget_class, GtdTaskRow, dnd_icon);
566 gtk_widget_class_bind_template_child (widget_class, GtdTaskRow, done_check);
567 gtk_widget_class_bind_template_child (widget_class, GtdTaskRow, edit_panel_revealer);
568 gtk_widget_class_bind_template_child (widget_class, GtdTaskRow, header_event_box);
569 gtk_widget_class_bind_template_child (widget_class, GtdTaskRow, main_box);
570 gtk_widget_class_bind_template_child (widget_class, GtdTaskRow, star_widget);
571 gtk_widget_class_bind_template_child (widget_class, GtdTaskRow, task_date_label);
572 gtk_widget_class_bind_template_child (widget_class, GtdTaskRow, task_list_label);
573 gtk_widget_class_bind_template_child (widget_class, GtdTaskRow, title_entry);
574
575 gtk_widget_class_bind_template_callback (widget_class, on_button_press_event_cb);
576 gtk_widget_class_bind_template_callback (widget_class, on_complete_check_toggled_cb);
577 gtk_widget_class_bind_template_callback (widget_class, on_key_pressed_cb);
578 gtk_widget_class_bind_template_callback (widget_class, on_remove_task_cb);
579 gtk_widget_class_bind_template_callback (widget_class, on_task_changed_cb);
580
581 gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
582 gtk_widget_class_set_css_name (widget_class, "taskrow");
583 }
584
585 static void
gtd_task_row_init(GtdTaskRow * self)586 gtd_task_row_init (GtdTaskRow *self)
587 {
588 GtkDragSource *drag_source;
589
590 self->bindings = g_ptr_array_new_with_free_func ((GDestroyNotify) g_binding_unbind);
591 self->active = FALSE;
592
593 gtk_widget_init_template (GTK_WIDGET (self));
594
595 drag_source = gtk_drag_source_new ();
596 gtk_drag_source_set_actions (drag_source, GDK_ACTION_MOVE);
597
598 g_signal_connect (drag_source, "prepare", G_CALLBACK (on_drag_prepare_cb), self);
599 g_signal_connect (drag_source, "drag-begin", G_CALLBACK (on_drag_begin_cb), self);
600 g_signal_connect (drag_source, "drag-cancel", G_CALLBACK (on_drag_cancelled_cb), self);
601
602 gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (drag_source));
603
604 gtk_widget_set_cursor_from_name (self->dnd_icon, "grab");
605 gtk_widget_set_cursor_from_name (self->header_event_box, "pointer");
606 }
607
608 GtkWidget*
gtd_task_row_new(GtdTask * task,GtdMarkdownRenderer * renderer)609 gtd_task_row_new (GtdTask *task,
610 GtdMarkdownRenderer *renderer)
611 {
612 return g_object_new (GTD_TYPE_TASK_ROW,
613 "task", task,
614 "renderer", renderer,
615 NULL);
616 }
617
618 void
gtd_task_row_set_task(GtdTaskRow * self,GtdTask * task)619 gtd_task_row_set_task (GtdTaskRow *self,
620 GtdTask *task)
621 {
622 GBinding *binding;
623 GtdTask *old_task;
624
625 g_return_if_fail (GTD_IS_TASK_ROW (self));
626
627 old_task = self->task;
628
629 if (old_task)
630 {
631 g_signal_handlers_disconnect_by_func (old_task, on_complete_changed_cb, self);
632 g_signal_handlers_disconnect_by_func (old_task, on_task_important_changed_cb, self);
633 g_signal_handlers_disconnect_by_func (old_task, on_star_widget_activated_cb, self);
634 g_ptr_array_set_size (self->bindings, 0);
635 }
636
637 g_set_object (&self->task, task);
638
639 if (task)
640 {
641 gtk_label_set_label (self->task_list_label,
642 gtd_task_list_get_name (gtd_task_get_list (task)));
643
644 g_signal_handlers_block_by_func (self->title_entry, on_task_changed_cb, self);
645 g_signal_handlers_block_by_func (self->done_check, on_complete_check_toggled_cb, self);
646
647 binding = g_object_bind_property (task,
648 "loading",
649 self,
650 "sensitive",
651 G_BINDING_DEFAULT |
652 G_BINDING_INVERT_BOOLEAN |
653 G_BINDING_SYNC_CREATE);
654 g_ptr_array_add (self->bindings, binding);
655
656 binding = g_object_bind_property (task,
657 "title",
658 self->title_entry,
659 "text",
660 G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
661 g_ptr_array_add (self->bindings, binding);
662
663 binding = g_object_bind_property_full (task,
664 "due-date",
665 self->task_date_label,
666 "label",
667 G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE,
668 date_to_label_binding_cb,
669 NULL,
670 self,
671 NULL);
672 g_ptr_array_add (self->bindings, binding);
673
674 on_complete_changed_cb (self, NULL, task);
675 g_signal_connect_object (task,
676 "notify::complete",
677 G_CALLBACK (on_complete_changed_cb),
678 self,
679 G_CONNECT_SWAPPED);
680
681 on_task_important_changed_cb (task, NULL, self);
682 g_signal_connect_object (task,
683 "notify::important",
684 G_CALLBACK (on_task_important_changed_cb),
685 self,
686 0);
687
688 g_signal_connect_object (self->star_widget,
689 "notify::active",
690 G_CALLBACK (on_star_widget_activated_cb),
691 self,
692 0);
693
694 g_signal_handlers_unblock_by_func (self->done_check, on_complete_check_toggled_cb, self);
695 g_signal_handlers_unblock_by_func (self->title_entry, on_task_changed_cb, self);
696 }
697 }
698
699 /**
700 * gtd_task_row_get_task:
701 * @row: a #GtdTaskRow
702 *
703 * Retrieves the #GtdTask that @row manages, or %NULL if none
704 * is set.
705 *
706 * Returns: (transfer none): the internal task of @row
707 */
708 GtdTask*
gtd_task_row_get_task(GtdTaskRow * row)709 gtd_task_row_get_task (GtdTaskRow *row)
710 {
711 g_return_val_if_fail (GTD_IS_TASK_ROW (row), NULL);
712
713 return row->task;
714 }
715
716 /**
717 * gtd_task_row_set_list_name_visible:
718 * @row: a #GtdTaskRow
719 * @show_list_name: %TRUE to show the list name, %FALSE to hide it
720 *
721 * Sets @row's list name label visibility to @show_list_name.
722 */
723 void
gtd_task_row_set_list_name_visible(GtdTaskRow * row,gboolean show_list_name)724 gtd_task_row_set_list_name_visible (GtdTaskRow *row,
725 gboolean show_list_name)
726 {
727 g_return_if_fail (GTD_IS_TASK_ROW (row));
728
729 gtk_widget_set_visible (GTK_WIDGET (row->task_list_label), show_list_name);
730 }
731
732 /**
733 * gtd_task_row_set_due_date_visible:
734 * @row: a #GtdTaskRow
735 * @show_due_date: %TRUE to show the due, %FALSE to hide it
736 *
737 * Sets @row's due date label visibility to @show_due_date.
738 */
739 void
gtd_task_row_set_due_date_visible(GtdTaskRow * row,gboolean show_due_date)740 gtd_task_row_set_due_date_visible (GtdTaskRow *row,
741 gboolean show_due_date)
742 {
743 g_return_if_fail (GTD_IS_TASK_ROW (row));
744
745 gtk_widget_set_visible (GTK_WIDGET (row->task_date_label), show_due_date);
746 }
747
748 gboolean
gtd_task_row_get_active(GtdTaskRow * self)749 gtd_task_row_get_active (GtdTaskRow *self)
750 {
751 g_return_val_if_fail (GTD_IS_TASK_ROW (self), FALSE);
752
753 return self->active;
754 }
755
756 void
gtd_task_row_set_active(GtdTaskRow * self,gboolean active)757 gtd_task_row_set_active (GtdTaskRow *self,
758 gboolean active)
759 {
760 g_return_if_fail (GTD_IS_TASK_ROW (self));
761
762 if (self->active == active)
763 return;
764
765 self->active = active;
766
767 /* Create or destroy the edit panel */
768 if (active && !self->edit_pane)
769 {
770 GTD_TRACE_MSG ("Creating edit pane");
771
772 self->edit_pane = GTD_EDIT_PANE (gtd_edit_pane_new ());
773 gtd_edit_pane_set_markdown_renderer (self->edit_pane, self->renderer);
774 gtd_edit_pane_set_task (self->edit_pane, self->task);
775
776 gtk_revealer_set_child (GTK_REVEALER (self->edit_panel_revealer), GTK_WIDGET (self->edit_pane));
777 gtk_widget_show (GTK_WIDGET (self->edit_pane));
778
779 g_signal_connect_swapped (self->edit_pane, "changed", G_CALLBACK (on_task_changed_cb), self);
780 g_signal_connect (self->edit_pane, "remove-task", G_CALLBACK (on_remove_task_cb), self);
781 }
782 else if (!active && self->edit_pane)
783 {
784 GTD_TRACE_MSG ("Destroying edit pane");
785
786 gtk_revealer_set_child (GTK_REVEALER (self->edit_panel_revealer), NULL);
787 self->edit_pane = NULL;
788 }
789
790 /* And reveal or hide it */
791 gtk_revealer_set_reveal_child (GTK_REVEALER (self->edit_panel_revealer), active);
792
793 /* Save the task if it is not being loaded */
794 if (!active && !gtd_object_get_loading (GTD_OBJECT (self->task)) && self->changed)
795 {
796 g_debug ("Saving task…");
797
798 gtd_provider_update_task (gtd_task_get_provider (self->task),
799 self->task,
800 NULL,
801 on_task_updated_cb,
802 self);
803 self->changed = FALSE;
804 }
805
806 if (active)
807 gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (self)), "active");
808 else
809 gtk_style_context_remove_class (gtk_widget_get_style_context (GTK_WIDGET (self)), "active");
810
811 g_signal_emit (self, active ? signals[ENTER] : signals[EXIT], 0);
812 }
813
814 void
gtd_task_row_set_sizegroups(GtdTaskRow * self,GtkSizeGroup * name_group,GtkSizeGroup * date_group)815 gtd_task_row_set_sizegroups (GtdTaskRow *self,
816 GtkSizeGroup *name_group,
817 GtkSizeGroup *date_group)
818 {
819 gtk_size_group_add_widget (name_group, GTK_WIDGET (self->task_list_label));
820 gtk_size_group_add_widget (name_group, GTK_WIDGET (self->task_date_label));
821 }
822