summaryrefslogtreecommitdiff
path: root/src/plugins/task-lists-workspace/gtd-task-lists-workspace.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/task-lists-workspace/gtd-task-lists-workspace.c')
-rw-r--r--src/plugins/task-lists-workspace/gtd-task-lists-workspace.c711
1 files changed, 711 insertions, 0 deletions
diff --git a/src/plugins/task-lists-workspace/gtd-task-lists-workspace.c b/src/plugins/task-lists-workspace/gtd-task-lists-workspace.c
new file mode 100644
index 0000000..407de89
--- /dev/null
+++ b/src/plugins/task-lists-workspace/gtd-task-lists-workspace.c
@@ -0,0 +1,711 @@
+/* gtd-task-lists-workspace.c
+ *
+ * Copyright 2020 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "GtdTaskListsWorkspace"
+
+#include "gtd-task-lists-workspace.h"
+
+#include "gtd-debug.h"
+#include "task-lists-workspace.h"
+#include "gtd-sidebar.h"
+#include "gtd-task-list-panel.h"
+
+#include <libpeas/peas.h>
+#include <glib/gi18n.h>
+
+struct _GtdTaskListsWorkspace
+{
+ GtkBox parent;
+
+ GtkWidget *back_button;
+ GtkWidget *content_box;
+ GtkMenuButton *gear_menu_button;
+ AdwLeaflet *leaflet;
+ GtkWidget *new_list_button;
+ GtkBox *panel_box_end;
+ GtkBox *panel_box_start;
+ GtkMenuButton *primary_menu_button;
+ GtkStack *stack;
+ GtdSidebar *sidebar;
+ GtkWidget *sidebar_box;
+
+ AdwToastOverlay *sidebar_overlay;
+ AdwToastOverlay *content_overlay;
+
+ GtdPanel *active_panel;
+ GtdPanel *task_list_panel;
+
+ GHashTable *notification_list;
+
+ PeasExtensionSet *panels_set;
+ GSimpleActionGroup *action_group;
+};
+
+typedef struct
+{
+ GtdWindow *window;
+ gchar *primary_text;
+ gchar *secondary_text;
+} ErrorData;
+
+static void gtd_workspace_iface_init (GtdWorkspaceInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GtdTaskListsWorkspace, gtd_task_lists_workspace, GTK_TYPE_BOX,
+ G_IMPLEMENT_INTERFACE (GTD_TYPE_WORKSPACE, gtd_workspace_iface_init))
+
+enum
+{
+ PROP_0,
+ PROP_ICON,
+ PROP_TITLE,
+ N_PROPS
+};
+
+enum
+{
+ PANEL_ADDED,
+ PANEL_REMOVED,
+ NUM_SIGNALS
+};
+
+static guint signals[NUM_SIGNALS] = { 0, };
+
+
+/*
+ * Auxiliary methods
+ */
+
+static void
+error_data_free (ErrorData *error_data)
+{
+ g_free (error_data->primary_text);
+ g_free (error_data->secondary_text);
+ g_free (error_data);
+}
+
+static void
+add_widgets (GtdTaskListsWorkspace *self,
+ GList *widgets)
+{
+ GList *l;
+
+ for (l = widgets; l; l = l->next)
+ {
+ switch (gtk_widget_get_halign (l->data))
+ {
+ case GTK_ALIGN_END:
+ gtk_box_append (self->panel_box_end, l->data);
+ break;
+
+ case GTK_ALIGN_START:
+ case GTK_ALIGN_BASELINE:
+ case GTK_ALIGN_FILL:
+ default:
+ gtk_box_append (self->panel_box_start, l->data);
+ break;
+ }
+ }
+}
+
+static void
+remove_widgets (GtdTaskListsWorkspace *self,
+ GList *widgets)
+{
+ GList *l;
+
+ for (l = widgets; l; l = l->next)
+ {
+ GtkBox *box;
+
+ if (gtk_widget_get_halign (l->data) == GTK_ALIGN_END)
+ box = self->panel_box_end;
+ else
+ box = self->panel_box_start;
+
+ g_object_ref (l->data);
+ gtk_box_remove (box, l->data);
+ }
+}
+
+static void
+update_panel_menu (GtdTaskListsWorkspace *self)
+{
+ GtkPopover *popover;
+ const GMenu *menu;
+
+ popover = gtd_panel_get_popover (self->active_panel);
+ menu = gtd_panel_get_menu (self->active_panel);
+
+ gtk_widget_set_visible (GTK_WIDGET (self->gear_menu_button), popover || menu);
+
+ if (popover)
+ {
+ gtk_menu_button_set_popover (self->gear_menu_button, GTK_WIDGET (popover));
+ }
+ else
+ {
+ gtk_menu_button_set_popover (self->gear_menu_button, NULL);
+ gtk_menu_button_set_menu_model (self->gear_menu_button, G_MENU_MODEL (menu));
+ }
+}
+
+
+/*
+ * Callbacks
+ */
+
+static void
+on_action_activate_panel_activated_cb (GSimpleAction *simple,
+ GVariant *parameters,
+ gpointer user_data)
+{
+ GtdTaskListsWorkspace *self;
+ g_autoptr (GVariant) panel_parameters = NULL;
+ g_autofree gchar *panel_id = NULL;
+ GtdPanel *panel;
+
+ self = GTD_TASK_LISTS_WORKSPACE (user_data);
+
+ g_variant_get (parameters,
+ "(sv)",
+ &panel_id,
+ &panel_parameters);
+
+ g_debug ("Activating panel '%s'", panel_id);
+
+ panel = (GtdPanel *) gtk_stack_get_child_by_name (self->stack, panel_id);
+ g_return_if_fail (panel && GTD_IS_PANEL (panel));
+
+ gtd_panel_activate (panel, panel_parameters);
+
+ gtk_stack_set_visible_child (self->stack, GTK_WIDGET (panel));
+ adw_leaflet_navigate (self->leaflet, ADW_NAVIGATION_DIRECTION_FORWARD);
+}
+
+static void
+on_action_toggle_archive_activated_cb (GSimpleAction *simple,
+ GVariant *state,
+ gpointer user_data)
+{
+ GtdTaskListsWorkspace *self;
+ gboolean archive_visible;
+
+ self = GTD_TASK_LISTS_WORKSPACE (user_data);
+ archive_visible = g_variant_get_boolean (state);
+
+ gtk_widget_set_visible (self->new_list_button, !archive_visible);
+ gtd_sidebar_set_archive_visible (self->sidebar, archive_visible);
+}
+
+static void
+on_back_sidebar_button_clicked_cb (GtkButton *button,
+ GtdTaskListsWorkspace *self)
+{
+ adw_leaflet_navigate (self->leaflet, ADW_NAVIGATION_DIRECTION_BACK);
+}
+
+static void
+on_back_button_clicked_cb (GtkButton *button,
+ GtdTaskListsWorkspace *self)
+{
+ gtk_widget_activate_action (GTK_WIDGET (self),
+ "task-lists-workspace.toggle-archive",
+ "b",
+ FALSE);
+}
+
+static void
+on_panel_added_cb (PeasExtensionSet *extension_set,
+ PeasPluginInfo *plugin_info,
+ GtdPanel *panel,
+ GtdTaskListsWorkspace *self)
+{
+ gtk_stack_add_titled (self->stack,
+ GTK_WIDGET (g_object_ref_sink (panel)),
+ gtd_panel_get_panel_name (panel),
+ gtd_panel_get_panel_title (panel));
+
+ g_signal_emit (self, signals[PANEL_ADDED], 0, panel);
+}
+
+static void
+on_panel_removed_cb (PeasExtensionSet *extension_set,
+ PeasPluginInfo *plugin_info,
+ GtdPanel *panel,
+ GtdTaskListsWorkspace *self)
+{
+ g_object_ref (panel);
+
+ gtk_stack_remove (self->stack, GTK_WIDGET (panel));
+ g_signal_emit (self, signals[PANEL_REMOVED], 0, panel);
+
+ g_object_unref (panel);
+}
+
+static void
+on_panel_menu_changed_cb (GObject *object,
+ GParamSpec *pspec,
+ GtdTaskListsWorkspace *self)
+{
+ if (GTD_PANEL (object) != self->active_panel)
+ return;
+
+ update_panel_menu (self);
+}
+static void
+on_stack_visible_child_cb (GtdTaskListsWorkspace *self,
+ GParamSpec *pspec,
+ GtkStack *stack)
+{
+ GtkWidget *visible_child;
+ GtdPanel *panel;
+ GList *header_widgets;
+
+ GTD_ENTRY;
+
+ visible_child = gtk_stack_get_visible_child (stack);
+ panel = GTD_PANEL (visible_child);
+
+ /* Remove previous panel's widgets */
+ if (self->active_panel)
+ {
+ header_widgets = gtd_panel_get_header_widgets (self->active_panel);
+
+ /* Disconnect signals */
+ g_signal_handlers_disconnect_by_func (self->active_panel,
+ on_panel_menu_changed_cb,
+ self);
+
+ remove_widgets (self, header_widgets);
+
+ g_list_free (header_widgets);
+ }
+
+ /* Add current panel's header widgets */
+ header_widgets = gtd_panel_get_header_widgets (panel);
+ add_widgets (self, header_widgets);
+
+ g_list_free (header_widgets);
+
+ g_signal_connect (panel, "notify::menu", G_CALLBACK (on_panel_menu_changed_cb), self);
+
+ /* Set panel as the new active panel */
+ g_set_object (&self->active_panel, panel);
+
+ /* Setup the panel's menu */
+ update_panel_menu (self);
+
+ GTD_EXIT;
+}
+
+void
+on_toast_dismissed_cb (AdwToast *self,
+ gpointer user_data)
+{
+ GtdNotification *notification = user_data;
+
+ GTD_ENTRY;
+
+ gtd_notification_execute_dismissal_action (notification);
+
+ GTD_EXIT;
+}
+
+void
+toast_activated_cb (GtdTaskListsWorkspace *self,
+ const char *action_name,
+ GVariant *parameter)
+{
+ AdwToast *toast;
+ GtdNotification *notification;
+
+ GTD_ENTRY;
+
+ toast = g_hash_table_lookup (self->notification_list, g_variant_get_string (parameter, NULL));
+
+ if (toast != NULL) {
+ notification = g_object_get_data (G_OBJECT (toast), "notification");
+
+ g_signal_handlers_block_by_func (toast, on_toast_dismissed_cb, notification);
+ gtd_notification_execute_secondary_action (notification);
+ }
+
+ GTD_EXIT;
+}
+
+static void
+on_show_notification_cb (GtdManager *manager,
+ GtdNotification *notification,
+ GtdTaskListsWorkspace *self)
+{
+ AdwToast *toast;
+ GValue btn_label = G_VALUE_INIT;
+
+ g_object_get_property (G_OBJECT (notification), "secondary-action-name", &btn_label);
+
+ /* Convert GtdNotification to AdwToast */
+ toast = adw_toast_new (gtd_notification_get_text (notification));
+ adw_toast_set_button_label (toast, g_value_get_string (&btn_label));
+ adw_toast_set_action_name (toast, "toast.activated");
+ adw_toast_set_action_target_value (toast, g_variant_new_string (g_value_get_string (&btn_label)));
+ g_object_set_data (G_OBJECT (toast), "notification", notification);
+
+ g_hash_table_insert (self->notification_list, (char *) g_value_get_string (&btn_label), toast);
+ g_signal_connect (toast, "dismissed", G_CALLBACK (on_toast_dismissed_cb), notification);
+
+ if (adw_leaflet_get_folded (self->leaflet)) {
+ if (adw_leaflet_get_visible_child (self->leaflet) == self->content_box)
+ adw_toast_overlay_add_toast (self->content_overlay, toast);
+ if (adw_leaflet_get_visible_child (self->leaflet) == self->sidebar_box)
+ adw_toast_overlay_add_toast (self->sidebar_overlay, toast);
+ } else {
+ adw_toast_overlay_add_toast (self->content_overlay, toast);
+ }
+}
+
+static void
+error_message_notification_primary_action (GtdNotification *notification,
+ gpointer user_data)
+{
+ error_data_free (user_data);
+}
+
+static void
+error_message_notification_secondary_action (GtdNotification *notification,
+ gpointer user_data)
+{
+ GtkWidget *dialog;
+ ErrorData *data;
+
+ data = user_data;
+ dialog = adw_message_dialog_new (GTK_WINDOW (data->window),
+ data->primary_text,
+ NULL);
+
+ adw_message_dialog_format_body (ADW_MESSAGE_DIALOG (dialog),
+ "%s",
+ data->secondary_text);
+
+ adw_message_dialog_add_response (ADW_MESSAGE_DIALOG (dialog),
+ "close", _("Close"));
+
+ g_signal_connect (dialog,
+ "response",
+ G_CALLBACK (gtk_window_destroy),
+ NULL);
+
+ gtk_widget_show (dialog);
+
+ error_data_free (data);
+}
+
+static void
+on_show_error_message_cb (GtdManager *manager,
+ const gchar *primary_text,
+ const gchar *secondary_text,
+ GtdNotificationActionFunc function,
+ gpointer user_data,
+ GtdTaskListsWorkspace *self)
+{
+ GtdNotification *notification;
+ ErrorData *error_data;
+
+ error_data = g_new0 (ErrorData, 1);
+ notification = gtd_notification_new (primary_text);
+
+ error_data->window = GTD_WINDOW (gtk_widget_get_root (GTK_WIDGET (self)));
+ error_data->primary_text = g_strdup (primary_text);
+ error_data->secondary_text = g_strdup (secondary_text);
+
+ gtd_notification_set_dismissal_action (notification,
+ error_message_notification_primary_action,
+ error_data);
+
+ if (!function)
+ {
+ gtd_notification_set_secondary_action (notification,
+ _("Details"),
+ error_message_notification_secondary_action,
+ error_data);
+ }
+ else
+ {
+ gtd_notification_set_secondary_action (notification, secondary_text, function, user_data);
+ }
+
+ gtd_manager_send_notification (gtd_manager_get_default (), notification);
+}
+
+/*
+ * GtdWorkspace implementation
+ */
+
+static const gchar*
+gtd_task_lists_workspace_get_id (GtdWorkspace *workspace)
+{
+ return "task-lists";
+}
+
+static const gchar*
+gtd_task_lists_workspace_get_title (GtdWorkspace *workspace)
+{
+ return _("Task Lists");
+}
+
+static gint
+gtd_task_lists_workspace_get_priority (GtdWorkspace *workspace)
+{
+ return 1000;
+}
+
+static GIcon*
+gtd_task_lists_workspace_get_icon (GtdWorkspace *workspace)
+{
+ return g_themed_icon_new ("view-list-symbolic");
+}
+
+static void
+gtd_task_lists_workspace_activate (GtdWorkspace *workspace,
+ GVariant *parameters)
+{
+ GtdTaskListsWorkspace *self = GTD_TASK_LISTS_WORKSPACE (workspace);
+
+ if (parameters)
+ {
+ const gchar *panel_id = g_variant_get_string (parameters, NULL);
+
+ g_debug ("Activating panel '%s'", panel_id);
+ gtk_stack_set_visible_child_name (self->stack, panel_id);
+ }
+ else
+ {
+ gtd_sidebar_activate (self->sidebar);
+ }
+}
+
+static void
+gtd_workspace_iface_init (GtdWorkspaceInterface *iface)
+{
+ iface->get_id = gtd_task_lists_workspace_get_id;
+ iface->get_title = gtd_task_lists_workspace_get_title;
+ iface->get_priority = gtd_task_lists_workspace_get_priority;
+ iface->get_icon = gtd_task_lists_workspace_get_icon;
+ iface->activate = gtd_task_lists_workspace_activate;
+}
+
+
+/*
+ * GObject overrides
+ */
+
+static void
+gtd_task_lists_workspace_dispose (GObject *object)
+{
+ GtdTaskListsWorkspace *self = (GtdTaskListsWorkspace *)object;
+
+ G_OBJECT_CLASS (gtd_task_lists_workspace_parent_class)->dispose (object);
+
+ g_signal_handlers_disconnect_by_func (self->panels_set, on_panel_added_cb, self);
+ g_signal_handlers_disconnect_by_func (self->panels_set, on_panel_removed_cb, self);
+ g_clear_object (&self->panels_set);
+}
+
+static void
+gtd_task_lists_workspace_constructed (GObject *object)
+{
+ GtdManager *manager = gtd_manager_get_default ();
+ GtdTaskListsWorkspace *self = (GtdTaskListsWorkspace *)object;
+
+ G_OBJECT_CLASS (gtd_task_lists_workspace_parent_class)->constructed (object);
+
+ /* Add loaded panels */
+ self->panels_set = peas_extension_set_new (peas_engine_get_default (),
+ GTD_TYPE_PANEL,
+ NULL);
+
+ peas_extension_set_foreach (self->panels_set,
+ (PeasExtensionSetForeachFunc) on_panel_added_cb,
+ self);
+
+ g_signal_connect (self->panels_set, "extension-added", G_CALLBACK (on_panel_added_cb), self);
+ g_signal_connect (self->panels_set, "extension-removed", G_CALLBACK (on_panel_removed_cb), self);
+ g_signal_connect (manager, "show-notification", G_CALLBACK (on_show_notification_cb), self);
+ g_signal_connect (manager, "show-error-message", G_CALLBACK (on_show_error_message_cb), self);
+}
+
+static void
+gtd_task_lists_workspace_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtdWorkspace *workspace = GTD_WORKSPACE (object);
+
+ switch (prop_id)
+ {
+ case PROP_ICON:
+ g_value_take_object (value, gtd_workspace_get_icon (workspace));
+ break;
+
+ case PROP_TITLE:
+ g_value_set_string (value, gtd_workspace_get_title (workspace));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gtd_task_lists_workspace_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+}
+
+static void
+gtd_task_lists_workspace_class_init (GtdTaskListsWorkspaceClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ g_resources_register (task_lists_workspace_get_resource ());
+
+ object_class->dispose = gtd_task_lists_workspace_dispose;
+ object_class->constructed = gtd_task_lists_workspace_constructed;
+ object_class->get_property = gtd_task_lists_workspace_get_property;
+ object_class->set_property = gtd_task_lists_workspace_set_property;
+
+
+ /**
+ * GtdTaskListsWorkspace::panel-added:
+ * @manager: a #GtdManager
+ * @panel: a #GtdPanel
+ *
+ * The ::panel-added signal is emmited after a #GtdPanel
+ * is added.
+ */
+ signals[PANEL_ADDED] = g_signal_new ("panel-added",
+ GTD_TYPE_TASK_LISTS_WORKSPACE,
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 1,
+ GTD_TYPE_PANEL);
+
+ /**
+ * GtdTaskListsWorkspace::panel-removed:
+ * @manager: a #GtdManager
+ * @panel: a #GtdPanel
+ *
+ * The ::panel-removed signal is emmited after a #GtdPanel
+ * is removed from the list.
+ */
+ signals[PANEL_REMOVED] = g_signal_new ("panel-removed",
+ GTD_TYPE_TASK_LISTS_WORKSPACE,
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 1,
+ GTD_TYPE_PANEL);
+
+ g_object_class_override_property (object_class, PROP_ICON, "icon");
+ g_object_class_override_property (object_class, PROP_TITLE, "title");
+
+ g_type_ensure (GTD_TYPE_PROVIDER_POPOVER);
+ g_type_ensure (GTD_TYPE_SIDEBAR);
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/todo/plugins/task-lists-workspace/gtd-task-lists-workspace.ui");
+
+ gtk_widget_class_bind_template_child (widget_class, GtdTaskListsWorkspace, back_button);
+ gtk_widget_class_bind_template_child (widget_class, GtdTaskListsWorkspace, content_box);
+ gtk_widget_class_bind_template_child (widget_class, GtdTaskListsWorkspace, gear_menu_button);
+ gtk_widget_class_bind_template_child (widget_class, GtdTaskListsWorkspace, leaflet);
+ gtk_widget_class_bind_template_child (widget_class, GtdTaskListsWorkspace, new_list_button);
+ gtk_widget_class_bind_template_child (widget_class, GtdTaskListsWorkspace, panel_box_end);
+ gtk_widget_class_bind_template_child (widget_class, GtdTaskListsWorkspace, panel_box_start);
+ gtk_widget_class_bind_template_child (widget_class, GtdTaskListsWorkspace, primary_menu_button);
+ gtk_widget_class_bind_template_child (widget_class, GtdTaskListsWorkspace, sidebar);
+ gtk_widget_class_bind_template_child (widget_class, GtdTaskListsWorkspace, sidebar_box);
+ gtk_widget_class_bind_template_child (widget_class, GtdTaskListsWorkspace, stack);
+ gtk_widget_class_bind_template_child (widget_class, GtdTaskListsWorkspace, sidebar_overlay);
+ gtk_widget_class_bind_template_child (widget_class, GtdTaskListsWorkspace, content_overlay);
+
+ gtk_widget_class_bind_template_callback (widget_class, on_back_button_clicked_cb);
+ gtk_widget_class_bind_template_callback (widget_class, on_back_sidebar_button_clicked_cb);
+ gtk_widget_class_bind_template_callback (widget_class, on_stack_visible_child_cb);
+
+ gtk_widget_class_install_action (widget_class, "toast.activated", "s", (GtkWidgetActionActivateFunc) toast_activated_cb);
+}
+
+static void
+gtd_task_lists_workspace_init (GtdTaskListsWorkspace *self)
+{
+ GtkApplication *application;
+ GMenu *primary_menu;
+
+ static const GActionEntry entries[] = {
+ { "activate-panel", on_action_activate_panel_activated_cb, "(sv)" },
+ { "toggle-archive", on_action_toggle_archive_activated_cb, "b" },
+ };
+
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ self->notification_list = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+ self->action_group = g_simple_action_group_new ();
+ g_action_map_add_action_entries (G_ACTION_MAP (self->action_group),
+ entries,
+ G_N_ELEMENTS (entries),
+ self);
+ gtk_widget_insert_action_group (GTK_WIDGET (self),
+ "task-lists-workspace",
+ G_ACTION_GROUP (self->action_group));
+
+ gtk_actionable_set_action_target_value (GTK_ACTIONABLE (self->back_button),
+ g_variant_new_boolean (FALSE));
+
+ /* Task list panel */
+ self->task_list_panel = GTD_PANEL (gtd_task_list_panel_new ());
+ on_panel_added_cb (NULL, NULL, self->task_list_panel, self);
+
+ gtd_sidebar_connect (self->sidebar, GTK_WIDGET (self));
+ gtd_sidebar_set_panel_stack (self->sidebar, self->stack);
+ gtd_sidebar_set_task_list_panel (self->sidebar, self->task_list_panel);
+
+ /* Fancy primary menu */
+ application = GTK_APPLICATION (g_application_get_default ());
+ primary_menu = gtk_application_get_menu_by_id (application, "primary-menu");
+ gtk_menu_button_set_menu_model (self->primary_menu_button, G_MENU_MODEL (primary_menu));
+}
+
+GtdWorkspace*
+gtd_task_lists_workspace_new (void)
+{
+ return g_object_new (GTD_TYPE_TASK_LISTS_WORKSPACE, NULL);
+}