From 5d8e439bc597159e3c9f0a8b65c0ae869dead3a8 Mon Sep 17 00:00:00 2001 From: Matthew Fennell Date: Sat, 27 Dec 2025 12:40:20 +0000 Subject: Import Upstream version 43.0 --- src/gui/gtd-task-row.c | 839 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 839 insertions(+) create mode 100644 src/gui/gtd-task-row.c (limited to 'src/gui/gtd-task-row.c') diff --git a/src/gui/gtd-task-row.c b/src/gui/gtd-task-row.c new file mode 100644 index 0000000..dd43d2b --- /dev/null +++ b/src/gui/gtd-task-row.c @@ -0,0 +1,839 @@ +/* gtd-task-row.c + * + * Copyright (C) 2015-2020 Georges Basile Stavracas Neto + * + * 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 . + */ + +#define G_LOG_DOMAIN "GtdTaskRow" + +#include "gtd-debug.h" +#include "gtd-edit-pane.h" +#include "gtd-manager.h" +#include "gtd-markdown-renderer.h" +#include "gtd-provider.h" +#include "gtd-star-widget.h" +#include "gtd-task-row.h" +#include "gtd-task.h" +#include "gtd-task-list.h" +#include "gtd-task-list-view.h" +#include "gtd-utils-private.h" +#include "gtd-widget.h" + +#include +#include +#include + +struct _GtdTaskRow +{ + GtkListBoxRow parent; + + /**/ + GtkWidget *content_box; + GtkWidget *main_box; + + GtkWidget *done_check; + GtkWidget *edit_panel_revealer; + GtkWidget *header_event_box; + GtdStarWidget *star_widget; + GtkWidget *title_entry; + + /* task widgets */ + GtkLabel *task_date_label; + GtkLabel *task_list_label; + + /* dnd widgets */ + GtkWidget *dnd_box; + GtkWidget *dnd_icon; + gint clicked_x; + gint clicked_y; + + /* data */ + GtdTask *task; + + GtdEditPane *edit_pane; + + GtdMarkdownRenderer *renderer; + GPtrArray *bindings; + + gboolean active; + gboolean changed; +}; + +#define PRIORITY_ICON_SIZE 8 + +static void on_star_widget_activated_cb (GtdStarWidget *star_widget, + GParamSpec *pspec, + GtdTaskRow *self); + +G_DEFINE_TYPE (GtdTaskRow, gtd_task_row, GTK_TYPE_LIST_BOX_ROW) + +enum +{ + ENTER, + EXIT, + REMOVE_TASK, + NUM_SIGNALS +}; + +enum +{ + PROP_0, + PROP_RENDERER, + PROP_TASK, + LAST_PROP +}; + +static guint signals[NUM_SIGNALS] = { 0, }; + + +/* + * Auxiliary methods + */ + +static gboolean +date_to_label_binding_cb (GBinding *binding, + const GValue *from_value, + GValue *to_value, + gpointer user_data) +{ + g_autofree gchar *new_label = NULL; + GDateTime *dt; + + g_return_val_if_fail (GTD_IS_TASK_ROW (user_data), FALSE); + + dt = g_value_get_boxed (from_value); + + if (dt) + { + g_autoptr (GDateTime) today = g_date_time_new_now_local (); + + if (g_date_time_get_year (dt) == g_date_time_get_year (today) && + g_date_time_get_month (dt) == g_date_time_get_month (today)) + { + if (g_date_time_get_day_of_month (dt) == g_date_time_get_day_of_month (today)) + { + new_label = g_strdup (_("Today")); + } + else if (g_date_time_get_day_of_month (dt) == g_date_time_get_day_of_month (today) + 1) + { + new_label = g_strdup (_("Tomorrow")); + } + else if (g_date_time_get_day_of_month (dt) == g_date_time_get_day_of_month (today) - 1) + { + new_label = g_strdup (_("Yesterday")); + } + else if (g_date_time_get_day_of_year (dt) > g_date_time_get_day_of_month (today) && + g_date_time_get_day_of_year (dt) < g_date_time_get_day_of_month (today) + 7) + { + new_label = g_date_time_format (dt, "%A"); + } + else + { + new_label = g_date_time_format (dt, "%x"); + } + } + else + { + new_label = g_date_time_format (dt, "%x"); + } + } + else + { + new_label = g_strdup (""); + } + + g_value_set_string (to_value, new_label); + + return TRUE; +} + +static GtkWidget* +create_transient_row (GtdTaskRow *self) +{ + GtdTaskRow *new_row; + + new_row = GTD_TASK_ROW (gtd_task_row_new (self->task, self->renderer)); + + gtk_widget_set_size_request (GTK_WIDGET (new_row), + gtk_widget_get_allocated_width (GTK_WIDGET (self)), + -1); + + gtk_revealer_set_reveal_child (GTK_REVEALER (new_row->edit_panel_revealer), self->active); + + gtk_widget_add_css_class (GTK_WIDGET (new_row), "background"); + gtk_widget_set_opacity (GTK_WIDGET (new_row), 0.66); + + return GTK_WIDGET (new_row); +} + + +/* + * Callbacks + */ + +static void +on_task_updated_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr (GError) error = NULL; + + gtd_provider_update_task_finish (GTD_PROVIDER (object), result, &error); + + if (error) + { + g_warning ("Error updating task: %s", error->message); + return; + } +} + +static void +on_remove_task_cb (GtdEditPane *edit_panel, + GtdTask *task, + GtdTaskRow *self) +{ + g_signal_emit (self, signals[REMOVE_TASK], 0); +} + +static void +on_button_press_event_cb (GtkGestureClick *gesture, + gint n_press, + gdouble x, + gdouble y, + GtdTaskRow *self) +{ + GtkWidget *widget; + gdouble real_x; + gdouble real_y; + + widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture)); + + gtk_widget_translate_coordinates (widget, + GTK_WIDGET (self), + x, + y, + &real_x, + &real_y); + + self->clicked_x = real_x; + self->clicked_y = real_y; + + GTD_TRACE_MSG ("GtkGestureClick:pressed received from a %s at %.1lf,%.1lf (%.1lf,%.1lf)", + G_OBJECT_TYPE_NAME (widget), + x, + y, + real_x, + real_y); +} + +static GdkContentProvider* +on_drag_prepare_cb (GtkDragSource *source, + gdouble x, + gdouble y, + GtdTaskRow *self) +{ + GTD_ENTRY; + GTD_RETURN (gdk_content_provider_new_typed (GTD_TYPE_TASK, self->task)); +} + +static void +on_drag_begin_cb (GtkDragSource *source, + GdkDrag *drag, + GtdTaskRow *self) +{ + GtkWidget *drag_icon; + GtkWidget *new_row; + GtkWidget *widget; + + GTD_ENTRY; + + widget = GTK_WIDGET (self); + + gtk_widget_set_cursor_from_name (widget, "grabbing"); + + new_row = create_transient_row (self); + drag_icon = gtk_drag_icon_get_for_drag (drag); + gtk_drag_icon_set_child (GTK_DRAG_ICON (drag_icon), new_row); + gdk_drag_set_hotspot (drag, self->clicked_x, self->clicked_y); + + gtk_widget_hide (widget); + + GTD_EXIT; +} + +static gboolean +on_drag_cancelled_cb (GtkDragSource *source, + GdkDrag *drag, + GdkDragCancelReason result, + GtdTaskRow *self) +{ + GTD_ENTRY; + + gtk_widget_set_cursor_from_name (GTK_WIDGET (self), NULL); + gtk_widget_show (GTK_WIDGET (self)); + + GTD_RETURN (FALSE); +} + +static void +on_complete_check_toggled_cb (GtkCheckButton *button, + GtdTaskRow *self) +{ + GTD_ENTRY; + + g_assert (GTD_IS_TASK (self->task)); + + gtd_task_row_set_active (self, FALSE); + + gtd_task_set_complete (self->task, gtk_check_button_get_active (button)); + gtd_provider_update_task (gtd_task_get_provider (self->task), + self->task, + NULL, + on_task_updated_cb, + self); + + GTD_EXIT; +} + +static void +on_complete_changed_cb (GtdTaskRow *self, + GParamSpec *pspec, + GtdTask *task) +{ + gboolean complete; + + complete = gtd_task_get_complete (task); + + if (complete) + gtk_widget_add_css_class (GTK_WIDGET (self), "complete"); + else + gtk_widget_remove_css_class (GTK_WIDGET (self), "complete"); + + /* Update the toggle button as well */ + g_signal_handlers_block_by_func (self->done_check, on_complete_check_toggled_cb, self); + gtk_check_button_set_active (GTK_CHECK_BUTTON (self->done_check), complete); + g_signal_handlers_unblock_by_func (self->done_check, on_complete_check_toggled_cb, self); +} + +static void +on_task_important_changed_cb (GtdTask *task, + GParamSpec *pspec, + GtdTaskRow *self) +{ + g_signal_handlers_block_by_func (self->star_widget, on_star_widget_activated_cb, self); + + gtd_star_widget_set_active (self->star_widget, gtd_task_get_important (task)); + + g_signal_handlers_unblock_by_func (self->star_widget, on_star_widget_activated_cb, self); +} + +static void +on_star_widget_activated_cb (GtdStarWidget *star_widget, + GParamSpec *pspec, + GtdTaskRow *self) +{ + g_signal_handlers_block_by_func (self->task, on_task_important_changed_cb, self); + + gtd_task_set_important (self->task, gtd_star_widget_get_active (star_widget)); + gtd_provider_update_task (gtd_task_get_provider (self->task), + self->task, + NULL, + on_task_updated_cb, + self); + + g_signal_handlers_unblock_by_func (self->task, on_task_important_changed_cb, self); +} + +static gboolean +on_key_pressed_cb (GtkEventControllerKey *controller, + guint keyval, + guint keycode, + GdkModifierType modifiers, + GtdTaskRow *self) +{ + GTD_ENTRY; + + /* Exit when pressing Esc without modifiers */ + if (keyval == GDK_KEY_Escape && !(modifiers & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) + { + GTD_TRACE_MSG ("Escape pressed, closing task…"); + gtd_task_row_set_active (self, FALSE); + } + + GTD_RETURN (GDK_EVENT_PROPAGATE); +} + +static void +on_task_changed_cb (GtdTaskRow *self) +{ + g_debug ("Task changed"); + + self->changed = TRUE; +} + + +/* + * GObject overrides + */ + +static void +gtd_task_row_finalize (GObject *object) +{ + GtdTaskRow *self = GTD_TASK_ROW (object); + + if (self->changed) + { + if (self->task) + { + gtd_provider_update_task (gtd_task_get_provider (self->task), + self->task, + NULL, + on_task_updated_cb, + self); + } + self->changed = FALSE; + } + + g_clear_object (&self->task); + + G_OBJECT_CLASS (gtd_task_row_parent_class)->finalize (object); +} + +static void +gtd_task_row_dispose (GObject *object) +{ + GtdTaskRow *self; + GtdTask *task; + + self = GTD_TASK_ROW (object); + task = self->task; + + g_clear_pointer (&self->bindings, g_ptr_array_unref); + + if (task) + g_signal_handlers_disconnect_by_func (task, on_complete_changed_cb, self); + + G_OBJECT_CLASS (gtd_task_row_parent_class)->dispose (object); +} + +static void +gtd_task_row_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtdTaskRow *self = GTD_TASK_ROW (object); + + switch (prop_id) + { + case PROP_RENDERER: + g_value_set_object (value, self->renderer); + break; + + case PROP_TASK: + g_value_set_object (value, self->task); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +gtd_task_row_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtdTaskRow *self = GTD_TASK_ROW (object); + + switch (prop_id) + { + case PROP_RENDERER: + self->renderer = g_value_get_object (value); + break; + + case PROP_TASK: + gtd_task_row_set_task (self, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +gtd_task_row_class_init (GtdTaskRowClass *klass) +{ + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = gtd_task_row_dispose; + object_class->finalize = gtd_task_row_finalize; + object_class->get_property = gtd_task_row_get_property; + object_class->set_property = gtd_task_row_set_property; + + /** + * GtdTaskRow::renderer: + * + * The internal markdown renderer. + */ + g_object_class_install_property ( + object_class, + PROP_RENDERER, + g_param_spec_object ("renderer", + "Renderer", + "Renderer", + GTD_TYPE_MARKDOWN_RENDERER, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + /** + * GtdTaskRow::task: + * + * The task that this row represents, or %NULL. + */ + g_object_class_install_property ( + object_class, + PROP_TASK, + g_param_spec_object ("task", + "Task of the row", + "The task that this row represents", + GTD_TYPE_TASK, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); + + /** + * GtdTaskRow::enter: + * + * Emitted when the row is focused and in the editing state. + */ + signals[ENTER] = g_signal_new ("enter", + GTD_TYPE_TASK_ROW, + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 0); + + /** + * GtdTaskRow::exit: + * + * Emitted when the row is unfocused and leaves the editing state. + */ + signals[EXIT] = g_signal_new ("exit", + GTD_TYPE_TASK_ROW, + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 0); + + /** + * GtdTaskRow::remove-task: + * + * Emitted when the user wants to delete the task represented by this row. + */ + signals[REMOVE_TASK] = g_signal_new ("remove-task", + GTD_TYPE_TASK_ROW, + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 0); + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/todo/ui/gtd-task-row.ui"); + + gtk_widget_class_bind_template_child (widget_class, GtdTaskRow, content_box); + gtk_widget_class_bind_template_child (widget_class, GtdTaskRow, dnd_box); + gtk_widget_class_bind_template_child (widget_class, GtdTaskRow, dnd_icon); + gtk_widget_class_bind_template_child (widget_class, GtdTaskRow, done_check); + gtk_widget_class_bind_template_child (widget_class, GtdTaskRow, edit_panel_revealer); + gtk_widget_class_bind_template_child (widget_class, GtdTaskRow, header_event_box); + gtk_widget_class_bind_template_child (widget_class, GtdTaskRow, main_box); + gtk_widget_class_bind_template_child (widget_class, GtdTaskRow, star_widget); + gtk_widget_class_bind_template_child (widget_class, GtdTaskRow, task_date_label); + gtk_widget_class_bind_template_child (widget_class, GtdTaskRow, task_list_label); + gtk_widget_class_bind_template_child (widget_class, GtdTaskRow, title_entry); + + gtk_widget_class_bind_template_callback (widget_class, on_button_press_event_cb); + gtk_widget_class_bind_template_callback (widget_class, on_complete_check_toggled_cb); + gtk_widget_class_bind_template_callback (widget_class, on_key_pressed_cb); + gtk_widget_class_bind_template_callback (widget_class, on_remove_task_cb); + gtk_widget_class_bind_template_callback (widget_class, on_task_changed_cb); + + gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); + gtk_widget_class_set_css_name (widget_class, "taskrow"); +} + +static void +gtd_task_row_init (GtdTaskRow *self) +{ + GtkDragSource *drag_source; + + self->bindings = g_ptr_array_new_with_free_func ((GDestroyNotify) g_binding_unbind); + self->active = FALSE; + + gtk_widget_init_template (GTK_WIDGET (self)); + + drag_source = gtk_drag_source_new (); + gtk_drag_source_set_actions (drag_source, GDK_ACTION_MOVE); + + g_signal_connect (drag_source, "prepare", G_CALLBACK (on_drag_prepare_cb), self); + g_signal_connect (drag_source, "drag-begin", G_CALLBACK (on_drag_begin_cb), self); + g_signal_connect (drag_source, "drag-cancel", G_CALLBACK (on_drag_cancelled_cb), self); + + gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (drag_source)); + + gtk_widget_set_cursor_from_name (self->dnd_icon, "grab"); + gtk_widget_set_cursor_from_name (self->header_event_box, "pointer"); +} + +GtkWidget* +gtd_task_row_new (GtdTask *task, + GtdMarkdownRenderer *renderer) +{ + return g_object_new (GTD_TYPE_TASK_ROW, + "task", task, + "renderer", renderer, + NULL); +} + +void +gtd_task_row_set_task (GtdTaskRow *self, + GtdTask *task) +{ + GBinding *binding; + GtdTask *old_task; + + g_return_if_fail (GTD_IS_TASK_ROW (self)); + + old_task = self->task; + + if (old_task) + { + g_signal_handlers_disconnect_by_func (old_task, on_complete_changed_cb, self); + g_signal_handlers_disconnect_by_func (old_task, on_task_important_changed_cb, self); + g_signal_handlers_disconnect_by_func (old_task, on_star_widget_activated_cb, self); + g_ptr_array_set_size (self->bindings, 0); + } + + g_set_object (&self->task, task); + + if (task) + { + gtk_label_set_label (self->task_list_label, + gtd_task_list_get_name (gtd_task_get_list (task))); + + g_signal_handlers_block_by_func (self->title_entry, on_task_changed_cb, self); + g_signal_handlers_block_by_func (self->done_check, on_complete_check_toggled_cb, self); + + binding = g_object_bind_property (task, + "loading", + self, + "sensitive", + G_BINDING_DEFAULT | + G_BINDING_INVERT_BOOLEAN | + G_BINDING_SYNC_CREATE); + g_ptr_array_add (self->bindings, binding); + + binding = g_object_bind_property (task, + "title", + self->title_entry, + "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + g_ptr_array_add (self->bindings, binding); + + binding = g_object_bind_property_full (task, + "due-date", + self->task_date_label, + "label", + G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE, + date_to_label_binding_cb, + NULL, + self, + NULL); + g_ptr_array_add (self->bindings, binding); + + on_complete_changed_cb (self, NULL, task); + g_signal_connect_object (task, + "notify::complete", + G_CALLBACK (on_complete_changed_cb), + self, + G_CONNECT_SWAPPED); + + on_task_important_changed_cb (task, NULL, self); + g_signal_connect_object (task, + "notify::important", + G_CALLBACK (on_task_important_changed_cb), + self, + 0); + + g_signal_connect_object (self->star_widget, + "notify::active", + G_CALLBACK (on_star_widget_activated_cb), + self, + 0); + + g_signal_handlers_unblock_by_func (self->done_check, on_complete_check_toggled_cb, self); + g_signal_handlers_unblock_by_func (self->title_entry, on_task_changed_cb, self); + } +} + +/** + * gtd_task_row_get_task: + * @row: a #GtdTaskRow + * + * Retrieves the #GtdTask that @row manages, or %NULL if none + * is set. + * + * Returns: (transfer none): the internal task of @row + */ +GtdTask* +gtd_task_row_get_task (GtdTaskRow *row) +{ + g_return_val_if_fail (GTD_IS_TASK_ROW (row), NULL); + + return row->task; +} + +/** + * gtd_task_row_set_list_name_visible: + * @row: a #GtdTaskRow + * @show_list_name: %TRUE to show the list name, %FALSE to hide it + * + * Sets @row's list name label visibility to @show_list_name. + */ +void +gtd_task_row_set_list_name_visible (GtdTaskRow *row, + gboolean show_list_name) +{ + g_return_if_fail (GTD_IS_TASK_ROW (row)); + + gtk_widget_set_visible (GTK_WIDGET (row->task_list_label), show_list_name); +} + +/** + * gtd_task_row_set_due_date_visible: + * @row: a #GtdTaskRow + * @show_due_date: %TRUE to show the due, %FALSE to hide it + * + * Sets @row's due date label visibility to @show_due_date. + */ +void +gtd_task_row_set_due_date_visible (GtdTaskRow *row, + gboolean show_due_date) +{ + g_return_if_fail (GTD_IS_TASK_ROW (row)); + + gtk_widget_set_visible (GTK_WIDGET (row->task_date_label), show_due_date); +} + +gboolean +gtd_task_row_get_active (GtdTaskRow *self) +{ + g_return_val_if_fail (GTD_IS_TASK_ROW (self), FALSE); + + return self->active; +} + +void +gtd_task_row_set_active (GtdTaskRow *self, + gboolean active) +{ + GDateTime *dt; + + g_return_if_fail (GTD_IS_TASK_ROW (self)); + + if (self->active == active) + return; + + self->active = active; + + /* Create or destroy the edit panel */ + if (active && !self->edit_pane) + { + GTD_TRACE_MSG ("Creating edit pane"); + + self->edit_pane = GTD_EDIT_PANE (gtd_edit_pane_new ()); + gtd_edit_pane_set_markdown_renderer (self->edit_pane, self->renderer); + gtd_edit_pane_set_task (self->edit_pane, self->task); + + gtk_revealer_set_child (GTK_REVEALER (self->edit_panel_revealer), GTK_WIDGET (self->edit_pane)); + gtk_widget_show (GTK_WIDGET (self->edit_pane)); + + g_signal_connect_swapped (self->edit_pane, "changed", G_CALLBACK (on_task_changed_cb), self); + g_signal_connect (self->edit_pane, "remove-task", G_CALLBACK (on_remove_task_cb), self); + } + else if (!active && self->edit_pane) + { + /* For some reason, setting the due date before closing the Edit Row has + * the potential to cause the Task List to update, causing the active task row + * to become corrupted, and crash Endeavour. My guess is this is an upstream + * behaviour, so work around it by setting the task due date during close */ + dt = gtd_edit_pane_get_new_date_time (self->edit_pane); + + if (gtd_task_get_due_date (self->task) != dt) { + gtd_task_set_due_date (self->task, + gtd_edit_pane_get_new_date_time (self->edit_pane)); + } + + GTD_TRACE_MSG ("Destroying edit pane"); + + gtk_revealer_set_child (GTK_REVEALER (self->edit_panel_revealer), NULL); + self->edit_pane = NULL; + } + + /* And reveal or hide it */ + gtk_revealer_set_reveal_child (GTK_REVEALER (self->edit_panel_revealer), active); + + /* Toggle title editability */ + gtk_editable_set_editable (GTK_EDITABLE (self->title_entry), active); + gtk_widget_set_sensitive (GTK_WIDGET (self->title_entry), active); + + /* Save the task if it is not being loaded */ + if (!active && !gtd_object_get_loading (GTD_OBJECT (self->task)) && self->changed) + { + g_debug ("Saving task…"); + + gtd_provider_update_task (gtd_task_get_provider (self->task), + self->task, + NULL, + on_task_updated_cb, + self); + self->changed = FALSE; + } + + if (active) { + gtk_widget_add_css_class (GTK_WIDGET (self), "card"); + } else { + gtk_widget_remove_css_class (GTK_WIDGET (self), "card"); + } + + g_signal_emit (self, active ? signals[ENTER] : signals[EXIT], 0); +} + +void +gtd_task_row_set_sizegroups (GtdTaskRow *self, + GtkSizeGroup *name_group, + GtkSizeGroup *date_group) +{ + gtk_size_group_add_widget (name_group, GTK_WIDGET (self->task_list_label)); + gtk_size_group_add_widget (name_group, GTK_WIDGET (self->task_date_label)); +} -- cgit v1.2.3