[gnome-builder] recent: move recent projects into hidden plugin
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] recent: move recent projects into hidden plugin
- Date: Sat, 18 Nov 2017 03:48:36 +0000 (UTC)
commit 7024c6aff70a6ba520eff0dbe8c2eda99fad7b93
Author: Christian Hergert <chergert redhat com>
Date: Fri Nov 17 19:47:01 2017 -0800
recent: move recent projects into hidden plugin
This allows things to be implemented outside of libide directly
and use the same API as the newcomers section.
src/libide/greeter/ide-greeter-perspective.c | 516 +++----------------------
src/libide/greeter/ide-greeter-perspective.ui | 93 ++---
src/plugins/meson.build | 1 +
src/plugins/recent/gbp-recent-project-row.c | 387 ++++++++++++++++++
src/plugins/recent/gbp-recent-project-row.h | 34 ++
src/plugins/recent/gbp-recent-project-row.ui | 89 +++++
src/plugins/recent/gbp-recent-section.c | 212 ++++++++++
src/plugins/recent/gbp-recent-section.h | 29 ++
src/plugins/recent/gbp-recent-section.ui | 72 ++++
src/plugins/recent/meson.build | 16 +
src/plugins/recent/recent-plugin.c | 30 ++
src/plugins/recent/recent.gresource.xml | 10 +
src/plugins/recent/recent.plugin | 9 +
13 files changed, 989 insertions(+), 509 deletions(-)
---
diff --git a/src/libide/greeter/ide-greeter-perspective.c b/src/libide/greeter/ide-greeter-perspective.c
index f102f6b..e2336b2 100644
--- a/src/libide/greeter/ide-greeter-perspective.c
+++ b/src/libide/greeter/ide-greeter-perspective.c
@@ -39,8 +39,6 @@ struct _IdeGreeterPerspective
{
GtkBin parent_instance;
- DzlSignalGroup *signal_group;
- IdeRecentProjects *recent_projects;
DzlPatternSpec *pattern_spec;
PeasExtensionSet *genesis_set;
@@ -60,8 +58,6 @@ struct _IdeGreeterPerspective
GtkRevealer *info_bar_revealer;
GtkViewport *viewport;
GtkWidget *titlebar;
- GtkBox *my_projects_container;
- GtkListBox *my_projects_list_box;
GtkButton *open_button;
GtkButton *cancel_button;
GtkButton *remove_button;
@@ -90,14 +86,6 @@ G_DEFINE_TYPE_EXTENDED (IdeGreeterPerspective, ide_greeter_perspective, GTK_TYPE
G_IMPLEMENT_INTERFACE (IDE_TYPE_PERSPECTIVE,
ide_perspective_iface_init))
-enum {
- PROP_0,
- PROP_RECENT_PROJECTS,
- LAST_PROP
-};
-
-static GParamSpec *properties [LAST_PROP];
-
static GtkWidget *
ide_greeter_perspective_get_titlebar (IdePerspective *perspective)
{
@@ -125,66 +113,37 @@ ide_perspective_iface_init (IdePerspectiveInterface *iface)
}
static void
-ide_greeter_perspective_first_visible_cb (GtkWidget *widget,
- gpointer user_data)
+ide_greeter_perspective_activate_cb (PeasExtensionSet *set,
+ PeasPluginInfo *plugin_info,
+ PeasExtension *exten,
+ gpointer user_data)
{
- GtkWidget **row = user_data;
+ IdeGreeterSection *section = (IdeGreeterSection *)exten;
+ gboolean *handled = user_data;
+
+ g_assert (PEAS_IS_EXTENSION_SET (set));
+ g_assert (plugin_info != NULL);
+ g_assert (IDE_IS_GREETER_SECTION (section));
- if ((*row == NULL) && gtk_widget_get_child_visible (widget))
- *row = widget;
+ if (!*handled)
+ *handled = ide_greeter_section_activate_first (section);
}
static void
ide_greeter_perspective__search_entry_activate (IdeGreeterPerspective *self,
GtkSearchEntry *search_entry)
{
- GtkWidget *row = NULL;
+ gboolean handled = FALSE;
g_assert (IDE_IS_GREETER_PERSPECTIVE (self));
g_assert (GTK_IS_SEARCH_ENTRY (search_entry));
- gtk_container_foreach (GTK_CONTAINER (self->my_projects_list_box),
- ide_greeter_perspective_first_visible_cb,
- &row);
-
- if (row != NULL)
- g_signal_emit_by_name (row, "activate");
-}
-
-IdeRecentProjects *
-ide_greeter_perspective_get_recent_projects (IdeGreeterPerspective *self)
-{
- g_return_val_if_fail (IDE_IS_GREETER_PERSPECTIVE (self), NULL);
-
- return self->recent_projects;
-}
-
-static void
-ide_greeter_perspective_apply_filter_cb (GtkWidget *widget,
- gpointer user_data)
-{
- gboolean *visible = user_data;
-
- g_assert (IDE_IS_GREETER_PROJECT_ROW (widget));
-
- if (gtk_widget_get_child_visible (widget))
- *visible = TRUE;
-}
-
-static void
-ide_greeter_perspective_apply_filter (IdeGreeterPerspective *self,
- GtkListBox *list_box,
- GtkWidget *container)
-{
- gboolean visible = FALSE;
-
- g_assert (IDE_IS_GREETER_PERSPECTIVE (self));
- g_assert (GTK_IS_LIST_BOX (list_box));
- g_assert (GTK_IS_CONTAINER (container));
+ peas_extension_set_foreach (self->sections,
+ ide_greeter_perspective_activate_cb,
+ &handled);
- gtk_list_box_invalidate_filter (list_box);
- gtk_container_foreach (GTK_CONTAINER (list_box), ide_greeter_perspective_apply_filter_cb, &visible);
- gtk_widget_set_visible (GTK_WIDGET (container), visible);
+ if (!handled)
+ gdk_window_beep (gtk_widget_get_window (GTK_WIDGET (search_entry)));
}
static void
@@ -195,13 +154,16 @@ ide_greeter_perspective_filter_sections (PeasExtensionSet *set,
{
IdeGreeterPerspective *self = user_data;
IdeGreeterSection *section = (IdeGreeterSection *)exten;
+ gboolean has_child;
g_assert (PEAS_IS_EXTENSION_SET (set));
g_assert (plugin_info != NULL);
g_assert (IDE_IS_GREETER_SECTION (section));
g_assert (IDE_IS_GREETER_PERSPECTIVE (self));
- ide_greeter_section_filter (section, self->pattern_spec);
+ has_child = ide_greeter_section_filter (section, self->pattern_spec);
+
+ gtk_widget_set_visible (GTK_WIDGET (section), has_child);
}
static void
@@ -212,12 +174,9 @@ ide_greeter_perspective_apply_filter_all (IdeGreeterPerspective *self)
g_assert (IDE_IS_GREETER_PERSPECTIVE (self));
g_clear_pointer (&self->pattern_spec, dzl_pattern_spec_unref);
- if ((text = gtk_entry_get_text (GTK_ENTRY (self->search_entry))))
- self->pattern_spec = dzl_pattern_spec_new (text);
- ide_greeter_perspective_apply_filter (self,
- self->my_projects_list_box,
- GTK_WIDGET (self->my_projects_container));
+ if (NULL != (text = gtk_entry_get_text (GTK_ENTRY (self->search_entry))))
+ self->pattern_spec = dzl_pattern_spec_new (text);
if (self->sections != NULL)
peas_extension_set_foreach (self->sections,
@@ -235,215 +194,6 @@ ide_greeter_perspective__search_entry_changed (IdeGreeterPerspective *self,
ide_greeter_perspective_apply_filter_all (self);
}
-static gboolean
-row_focus_in_event (IdeGreeterPerspective *self,
- GdkEventFocus *focus,
- IdeGreeterProjectRow *row)
-{
- GtkAllocation alloc;
- GtkAllocation row_alloc;
- gint dest_x;
- gint dest_y;
-
- g_assert (IDE_IS_GREETER_PERSPECTIVE (self));
-
- gtk_widget_get_allocation (GTK_WIDGET (self->viewport), &alloc);
- gtk_widget_get_allocation (GTK_WIDGET (row), &row_alloc);
-
- /*
- * If we are smaller than the visible area, don't do anything for now.
- * This can happen during creation of the window and resize process.
- */
- if (row_alloc.height > alloc.height)
- return GDK_EVENT_PROPAGATE;
-
- if (gtk_widget_translate_coordinates (GTK_WIDGET (row), GTK_WIDGET (self->viewport), 0, 0, &dest_x,
&dest_y))
- {
- gint distance = 0;
-
- if (dest_y < 0)
- {
- distance = dest_y;
- }
- else if ((dest_y + row_alloc.height) > alloc.height)
- {
- distance = dest_y + row_alloc.height - alloc.height;
- }
-
- if (distance != 0)
- {
- GtkAdjustment *vadj;
- gdouble value;
-
- vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (self->viewport));
- value = gtk_adjustment_get_value (vadj);
- gtk_adjustment_set_value (vadj, value + distance);
- }
- }
-
- return GDK_EVENT_PROPAGATE;
-}
-
-static gboolean
-selection_to_true (GBinding *binding,
- const GValue *from_value,
- GValue *to_value,
- gpointer user_data)
-{
- if (G_VALUE_HOLDS_STRING (from_value) && G_VALUE_HOLDS_BOOLEAN (to_value))
- {
- const gchar *str;
-
- str = g_value_get_string (from_value);
- g_value_set_boolean (to_value, ide_str_equal0 (str, "selection"));
-
- return TRUE;
- }
-
- return FALSE;
-}
-
-static void
-ide_greeter_perspective__row_notify_selected (IdeGreeterPerspective *self,
- GParamSpec *pspec,
- IdeGreeterProjectRow *row)
-{
- gboolean selected = FALSE;
-
- g_assert (IDE_IS_GREETER_PERSPECTIVE (self));
- g_assert (pspec != NULL);
- g_assert (IDE_IS_GREETER_PROJECT_ROW (row));
-
- g_object_get (row, "selected", &selected, NULL);
- self->selected_count += selected ? 1 : -1;
-
- dzl_gtk_widget_action_set (GTK_WIDGET (self), "greeter", "delete-selected-rows",
- "enabled", self->selected_count > 0,
- NULL);
-}
-
-static void
-recent_projects_items_changed (IdeGreeterPerspective *self,
- guint position,
- guint removed,
- guint added,
- GListModel *list_model)
-{
- IdeGreeterProjectRow *row;
- gsize i;
-
- /*
- * TODO: We ignore removed out of simplicity for now.
- * But IdeRecentProjects doesn't currently remove anything anyway.
- */
-
- g_assert (IDE_IS_GREETER_PERSPECTIVE (self));
- g_assert (G_IS_LIST_MODEL (list_model));
- g_assert (IDE_IS_RECENT_PROJECTS (list_model));
-
- if (g_list_model_get_n_items (list_model) > 0)
- {
- if (ide_str_equal0 ("empty-state", gtk_stack_get_visible_child_name (self->stack)))
- gtk_stack_set_visible_child_name (self->stack, "projects");
- }
-
- for (i = 0; i < added; i++)
- {
- IdeProjectInfo *project_info;
-
- project_info = g_list_model_get_item (list_model, position + i);
-
- row = g_object_new (IDE_TYPE_GREETER_PROJECT_ROW,
- "visible", TRUE,
- "project-info", project_info,
- NULL);
- g_signal_connect_object (row,
- "focus-in-event",
- G_CALLBACK (row_focus_in_event),
- self,
- G_CONNECT_SWAPPED);
- g_signal_connect_object (row,
- "notify::selected",
- G_CALLBACK (ide_greeter_perspective__row_notify_selected),
- self,
- G_CONNECT_SWAPPED);
-
- if (ide_project_info_get_is_recent (project_info))
- {
- GtkListBox *list_box = self->my_projects_list_box;
-
- g_object_bind_property_full (self->state_machine, "state",
- row, "selection-mode",
- G_BINDING_SYNC_CREATE,
- selection_to_true, NULL,
- NULL, NULL);
- gtk_container_add (GTK_CONTAINER (list_box), GTK_WIDGET (row));
- }
- }
-
- ide_greeter_perspective_apply_filter_all (self);
-}
-
-static gint
-ide_greeter_perspective_sort_rows (GtkListBoxRow *row1,
- GtkListBoxRow *row2,
- gpointer user_data)
-{
- IdeProjectInfo *info1;
- IdeProjectInfo *info2;
-
- info1 = ide_greeter_project_row_get_project_info (IDE_GREETER_PROJECT_ROW (row1));
- info2 = ide_greeter_project_row_get_project_info (IDE_GREETER_PROJECT_ROW (row2));
-
- return ide_project_info_compare (info1, info2);
-}
-
-static void
-ide_greeter_perspective_set_recent_projects (IdeGreeterPerspective *self,
- IdeRecentProjects *recent_projects)
-{
- g_return_if_fail (IDE_IS_GREETER_PERSPECTIVE (self));
- g_return_if_fail (!recent_projects || IDE_IS_RECENT_PROJECTS (recent_projects));
-
- if (g_set_object (&self->recent_projects, recent_projects))
- {
- dzl_signal_group_set_target (self->signal_group, recent_projects);
-
- if (recent_projects != NULL)
- {
- GListModel *list_model;
- guint n_items;
-
- list_model = G_LIST_MODEL (recent_projects);
- n_items = g_list_model_get_n_items (list_model);
- recent_projects_items_changed (self, 0, 0, n_items, list_model);
- }
-
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_RECENT_PROJECTS]);
- }
-}
-
-static gboolean
-ide_greeter_perspective_filter_row (GtkListBoxRow *row,
- gpointer user_data)
-{
- IdeGreeterPerspective *self = user_data;
- IdeGreeterProjectRow *project_row = (IdeGreeterProjectRow *)row;
- const gchar *search_text;
- gboolean ret;
-
- g_assert (IDE_IS_GREETER_PERSPECTIVE (self));
- g_assert (IDE_IS_GREETER_PROJECT_ROW (project_row));
-
- if (self->pattern_spec == NULL)
- return TRUE;
-
- search_text = ide_greeter_project_row_get_search_text (project_row);
- ret = dzl_pattern_spec_match (self->pattern_spec, search_text);
-
- return ret;
-}
-
static void
ide_greeter_perspective_open_project_cb (GObject *object,
GAsyncResult *result,
@@ -482,102 +232,6 @@ ide_greeter_perspective_open_project_cb (GObject *object,
}
static void
-ide_greeter_perspective__row_activated (IdeGreeterPerspective *self,
- IdeGreeterProjectRow *row,
- GtkListBox *list_box)
-{
- IdeProjectInfo *project_info;
-
- g_assert (IDE_IS_GREETER_PERSPECTIVE (self));
- g_assert (IDE_IS_GREETER_PROJECT_ROW (row));
- g_assert (GTK_IS_LIST_BOX (list_box));
-
- if (dzl_state_machine_is_state (self->state_machine, "selection"))
- {
- gboolean selected = FALSE;
-
- g_object_get (row, "selected", &selected, NULL);
- g_object_set (row, "selected", !selected, NULL);
-
- return;
- }
-
- project_info = ide_greeter_project_row_get_project_info (row);
-
- ide_greeter_perspective_load_project (self, project_info);
-}
-
-static gboolean
-ide_greeter_perspective__keynav_failed (IdeGreeterPerspective *self,
- GtkDirectionType dir,
- GtkListBox *list_box)
-{
- g_assert (IDE_IS_GREETER_PERSPECTIVE (self));
- g_assert (GTK_IS_LIST_BOX (list_box));
-
-#if 0
- /* TODO: Navigate to icon grid */
- if ((list_box == self->my_projects_list_box) && (dir == GTK_DIR_DOWN))
- {
- gtk_widget_child_focus (GTK_WIDGET (self->other_projects_list_box), GTK_DIR_DOWN);
- return GDK_EVENT_STOP;
- }
- else if ((list_box == self->other_projects_list_box) && (dir == GTK_DIR_UP))
- {
- gtk_widget_child_focus (GTK_WIDGET (self->my_projects_list_box), GTK_DIR_UP);
- return GDK_EVENT_STOP;
- }
-#endif
-
- return GDK_EVENT_PROPAGATE;
-}
-
-static void
-delete_selected_rows (GSimpleAction *action,
- GVariant *parameter,
- gpointer user_data)
-{
- IdeGreeterPerspective *self = user_data;
- GList *rows;
- GList *iter;
- GList *projects = NULL;
-
- g_assert (IDE_IS_GREETER_PERSPECTIVE (self));
- g_assert (G_IS_SIMPLE_ACTION (action));
-
- rows = gtk_container_get_children (GTK_CONTAINER (self->my_projects_list_box));
-
- for (iter = rows; iter; iter = iter->next)
- {
- IdeGreeterProjectRow *row = iter->data;
- gboolean selected = FALSE;
-
- g_object_get (row, "selected", &selected, NULL);
-
- if (selected)
- {
- IdeProjectInfo *info;
-
- info = ide_greeter_project_row_get_project_info (row);
- projects = g_list_prepend (projects, g_object_ref (info));
- gtk_container_remove (GTK_CONTAINER (self->my_projects_list_box), iter->data);
- }
- }
-
- g_list_free (rows);
-
- ide_recent_projects_remove (self->recent_projects, projects);
- g_list_free_full (projects, g_object_unref);
-
- self->selected_count = 0;
- g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
-
- dzl_state_machine_set_state (self->state_machine, "browse");
-
- ide_greeter_perspective_apply_filter_all (self);
-}
-
-static void
ide_greeter_perspective_dialog_response (IdeGreeterPerspective *self,
gint response_id,
GtkFileChooserDialog *dialog)
@@ -589,7 +243,9 @@ ide_greeter_perspective_dialog_response (IdeGreeterPerspective *self,
{
IdeWorkbench *workbench;
- if (NULL != (workbench = ide_widget_get_workbench (GTK_WIDGET (self))))
+ workbench = ide_widget_get_workbench (GTK_WIDGET (self));
+
+ if (workbench != NULL)
{
g_autoptr(GFile) project_file = NULL;
@@ -597,7 +253,12 @@ ide_greeter_perspective_dialog_response (IdeGreeterPerspective *self,
gtk_widget_set_sensitive (GTK_WIDGET (self->titlebar), FALSE);
project_file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
- ide_workbench_open_project_async (workbench, project_file, NULL, NULL, NULL);
+
+ ide_workbench_open_project_async (workbench,
+ project_file,
+ NULL,
+ ide_greeter_perspective_open_project_cb,
+ g_object_ref (self));
}
}
@@ -802,8 +463,10 @@ ide_greeter_perspective_genesis_cancel_clicked (IdeGreeterPerspective *self,
g_assert (GTK_IS_BUTTON (genesis_cancel_button));
g_cancellable_cancel (self->cancellable);
- dzl_state_machine_set_state (self->state_machine, "browse");
ide_greeter_perspective_apply_filter_all (self);
+
+ /* TODO: If there are items, we need to go back to empty */
+ dzl_state_machine_set_state (self->state_machine, "browse");
}
static void
@@ -1269,7 +932,12 @@ ide_greeter_perspective_section_added (PeasExtensionSet *set,
GTK_WIDGET (section),
"priority", priority,
NULL);
- gtk_widget_show (GTK_WIDGET (section));
+
+ if (ide_greeter_section_filter (section, self->pattern_spec))
+ {
+ dzl_state_machine_set_state (self->state_machine, "browse");
+ gtk_widget_show (GTK_WIDGET (section));
+ }
IDE_EXIT;
}
@@ -1297,6 +965,8 @@ ide_greeter_perspective_section_removed (PeasExtensionSet *set,
gtk_container_remove (GTK_CONTAINER (self->sections_container),
GTK_WIDGET (section));
+ /* TODO: Might have to switch to empty state */
+
IDE_EXIT;
}
@@ -1304,13 +974,9 @@ static void
ide_greeter_perspective_constructed (GObject *object)
{
IdeGreeterPerspective *self = (IdeGreeterPerspective *)object;
- IdeRecentProjects *recent_projects;
G_OBJECT_CLASS (ide_greeter_perspective_parent_class)->constructed (object);
- recent_projects = ide_application_get_recent_projects (IDE_APPLICATION_DEFAULT);
- ide_greeter_perspective_set_recent_projects (self, recent_projects);
-
ide_greeter_perspective_load_genesis_addins (self);
self->sections = peas_extension_set_new (peas_engine_get_default (),
@@ -1330,6 +996,20 @@ ide_greeter_perspective_constructed (GObject *object)
}
static void
+delete_selected_rows (GSimpleAction *simple,
+ GVariant *param,
+ gpointer user_data)
+{
+ IdeGreeterPerspective *self = user_data;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_GREETER_PERSPECTIVE (self));
+
+ IDE_EXIT;
+}
+
+static void
ide_greeter_perspective_destroy (GtkWidget *widget)
{
IdeGreeterPerspective *self = (IdeGreeterPerspective *)widget;
@@ -1349,52 +1029,12 @@ ide_greeter_perspective_finalize (GObject *object)
ide_clear_weak_pointer (&self->ready_binding);
g_clear_pointer (&self->pattern_spec, dzl_pattern_spec_unref);
- g_clear_object (&self->signal_group);
- g_clear_object (&self->recent_projects);
g_clear_object (&self->cancellable);
G_OBJECT_CLASS (ide_greeter_perspective_parent_class)->finalize (object);
}
static void
-ide_greeter_perspective_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
-{
- IdeGreeterPerspective *self = IDE_GREETER_PERSPECTIVE (object);
-
- switch (prop_id)
- {
- case PROP_RECENT_PROJECTS:
- g_value_set_object (value, ide_greeter_perspective_get_recent_projects (self));
- break;
-
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- }
-}
-
-static void
-ide_greeter_perspective_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- IdeGreeterPerspective *self = IDE_GREETER_PERSPECTIVE (object);
-
- switch (prop_id)
- {
- case PROP_RECENT_PROJECTS:
- ide_greeter_perspective_set_recent_projects (self, g_value_get_object (value));
- break;
-
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- }
-}
-
-static void
ide_greeter_perspective_class_init (IdeGreeterPerspectiveClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
@@ -1402,20 +1042,9 @@ ide_greeter_perspective_class_init (IdeGreeterPerspectiveClass *klass)
object_class->finalize = ide_greeter_perspective_finalize;
object_class->constructed = ide_greeter_perspective_constructed;
- object_class->get_property = ide_greeter_perspective_get_property;
- object_class->set_property = ide_greeter_perspective_set_property;
widget_class->destroy = ide_greeter_perspective_destroy;
- properties [PROP_RECENT_PROJECTS] =
- g_param_spec_object ("recent-projects",
- "Recent Projects",
- "The recent projects that have been mined.",
- IDE_TYPE_RECENT_PROJECTS,
- (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- g_object_class_install_properties (object_class, LAST_PROP, properties);
-
gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/builder/ui/ide-greeter-perspective.ui");
gtk_widget_class_set_css_name (widget_class, "greeter");
gtk_widget_class_bind_template_child (widget_class, IdeGreeterPerspective, cancel_button);
@@ -1427,8 +1056,6 @@ ide_greeter_perspective_class_init (IdeGreeterPerspectiveClass *klass)
gtk_widget_class_bind_template_child (widget_class, IdeGreeterPerspective, info_bar);
gtk_widget_class_bind_template_child (widget_class, IdeGreeterPerspective, info_bar_label);
gtk_widget_class_bind_template_child (widget_class, IdeGreeterPerspective, info_bar_revealer);
- gtk_widget_class_bind_template_child (widget_class, IdeGreeterPerspective, my_projects_container);
- gtk_widget_class_bind_template_child (widget_class, IdeGreeterPerspective, my_projects_list_box);
gtk_widget_class_bind_template_child (widget_class, IdeGreeterPerspective, open_button);
gtk_widget_class_bind_template_child (widget_class, IdeGreeterPerspective, remove_button);
gtk_widget_class_bind_template_child (widget_class, IdeGreeterPerspective, scrolled_window);
@@ -1451,13 +1078,6 @@ ide_greeter_perspective_init (IdeGreeterPerspective *self)
g_autoptr(GSimpleActionGroup) group = NULL;
g_autoptr(GAction) state = NULL;
- self->signal_group = dzl_signal_group_new (IDE_TYPE_RECENT_PROJECTS);
- dzl_signal_group_connect_object (self->signal_group,
- "items-changed",
- G_CALLBACK (recent_projects_items_changed),
- self,
- G_CONNECT_SWAPPED);
-
gtk_widget_init_template (GTK_WIDGET (self));
g_signal_connect (self->titlebar,
@@ -1477,18 +1097,6 @@ ide_greeter_perspective_init (IdeGreeterPerspective *self)
self,
G_CONNECT_SWAPPED);
- g_signal_connect_object (self->my_projects_list_box,
- "row-activated",
- G_CALLBACK (ide_greeter_perspective__row_activated),
- self,
- G_CONNECT_SWAPPED);
-
- g_signal_connect_object (self->my_projects_list_box,
- "keynav-failed",
- G_CALLBACK (ide_greeter_perspective__keynav_failed),
- self,
- G_CONNECT_SWAPPED);
-
g_signal_connect_object (self->top_stack,
"notify::visible-child",
G_CALLBACK (ide_greeter_perspective_genesis_changed),
@@ -1525,14 +1133,6 @@ ide_greeter_perspective_init (IdeGreeterPerspective *self)
self,
G_CONNECT_SWAPPED);
- gtk_list_box_set_sort_func (self->my_projects_list_box,
- ide_greeter_perspective_sort_rows,
- NULL, NULL);
-
- gtk_list_box_set_filter_func (self->my_projects_list_box,
- ide_greeter_perspective_filter_row,
- self, NULL);
-
group = g_simple_action_group_new ();
state = dzl_state_machine_create_action (self->state_machine, "state");
g_action_map_add_action (G_ACTION_MAP (group), state);
diff --git a/src/libide/greeter/ide-greeter-perspective.ui b/src/libide/greeter/ide-greeter-perspective.ui
index 3411887..f47d37d 100644
--- a/src/libide/greeter/ide-greeter-perspective.ui
+++ b/src/libide/greeter/ide-greeter-perspective.ui
@@ -91,56 +91,6 @@
<property name="visible">true</property>
</object>
</child>
- <child>
- <object class="GtkBox" id="my_projects_container">
- <property name="orientation">vertical</property>
- <property name="spacing">6</property>
- <property name="visible">true</property>
- <child>
- <object class="GtkBox">
- <property name="visible">true</property>
- <child>
- <object class="GtkLabel" id="my_projects_label">
- <property name="label" translatable="yes">Recent
Projects</property>
- <property name="visible">true</property>
- <property name="xalign">0.0</property>
- <property name="hexpand">true</property>
- <style>
- <class name="dim-label"/>
- </style>
- <attributes>
- <attribute name="weight" value="bold"/>
- </attributes>
- </object>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="visible">true</property>
- <property name="label"
translatable="yes">Updated</property>
- <property name="margin-end">10</property>
- <property name="halign">end</property>
- <style>
- <class name="dim-label"/>
- </style>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="GtkFrame">
- <property name="halign">center</property>
- <property name="visible">true</property>
- <property name="width-request">600</property>
- <child>
- <object class="GtkListBox" id="my_projects_list_box">
- <property name="visible">true</property>
- <property name="selection-mode">none</property>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
</object>
</child>
</object>
@@ -227,6 +177,15 @@
</attributes>
</object>
</child>
+ <child>
+ <object class="GtkLabel" id="empty_title">
+ <property name="visible">true</property>
+ <property name="label" translatable="yes">Builder</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
</object>
</child>
<child>
@@ -305,7 +264,7 @@
</child>
</object>
<object class="DzlStateMachine" id="state_machine">
- <property name="state">browse</property>
+ <property name="state">empty</property>
<states>
<state name="browse">
<object id="titlebar">
@@ -335,6 +294,9 @@
<object id="genesis_continue_button">
<property name="visible">false</property>
</object>
+ <object id="stack">
+ <property name="visible-child-name">projects</property>
+ </object>
<object id="top_stack">
<property name="visible-child-name">greeter</property>
</object>
@@ -403,6 +365,35 @@
<property name="visible-child-name">genesis</property>
</object>
</state>
+ <state name="empty">
+ <object id="titlebar">
+ <property name="show-close-button">true</property>
+ </object>
+ <object id="action_bar">
+ <property name="visible">false</property>
+ </object>
+ <object id="title_stack">
+ <property name="visible-child">empty_title</property>
+ </object>
+ <object id="cancel_button">
+ <property name="visible">false</property>
+ </object>
+ <object id="selection_button">
+ <property name="visible">false</property>
+ </object>
+ <object id="genesis_buttons">
+ <property name="visible">true</property>
+ </object>
+ <object id="genesis_cancel_button">
+ <property name="visible">false</property>
+ </object>
+ <object id="genesis_continue_button">
+ <property name="visible">false</property>
+ </object>
+ <object id="stack">
+ <property name="visible-child-name">empty-state</property>
+ </object>
+ </state>
</states>
</object>
</interface>
diff --git a/src/plugins/meson.build b/src/plugins/meson.build
index 10eadee..aaa620e 100644
--- a/src/plugins/meson.build
+++ b/src/plugins/meson.build
@@ -54,6 +54,7 @@ subdir('project-tree')
subdir('python-gi-imports-completion')
subdir('python-pack')
subdir('quick-highlight')
+subdir('recent')
subdir('retab')
subdir('rust-langserv')
subdir('rustup')
diff --git a/src/plugins/recent/gbp-recent-project-row.c b/src/plugins/recent/gbp-recent-project-row.c
new file mode 100644
index 0000000..174ca4d
--- /dev/null
+++ b/src/plugins/recent/gbp-recent-project-row.c
@@ -0,0 +1,387 @@
+/* gbp-recent-project-row.c
+ *
+ * Copyright © 2015 Christian Hergert <christian hergert me>
+ *
+ * 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/>.
+ */
+
+#define G_LOG_DOMAIN "gbp-recent-project-row"
+
+#include <dazzle.h>
+#include <glib/gi18n.h>
+
+#include "gbp-recent-project-row.h"
+
+struct _GbpRecentProjectRow
+{
+ GtkListBoxRow parent_instance;
+
+ IdeProjectInfo *project_info;
+ DzlBindingGroup *bindings;
+ gchar *search_text;
+
+ GtkLabel *date_label;
+ GtkLabel *description_label;
+ GtkBox *tags_box;
+ GtkLabel *location_label;
+ GtkLabel *title_label;
+ GtkCheckButton *checkbox;
+};
+
+G_DEFINE_TYPE (GbpRecentProjectRow, gbp_recent_project_row, GTK_TYPE_LIST_BOX_ROW)
+
+enum {
+ PROP_0,
+ PROP_PROJECT_INFO,
+ PROP_SELECTED,
+ PROP_SELECTION_MODE,
+ LAST_PROP
+};
+
+static GParamSpec *properties [LAST_PROP];
+static GFile *home_dir;
+
+void
+gbp_recent_project_row_set_selection_mode (GbpRecentProjectRow *self,
+ gboolean selection_mode)
+{
+ g_return_if_fail (GBP_IS_RECENT_PROJECT_ROW (self));
+
+ gtk_widget_set_visible (GTK_WIDGET (self->checkbox), selection_mode);
+}
+
+IdeProjectInfo *
+gbp_recent_project_row_get_project_info (GbpRecentProjectRow *self)
+{
+ g_return_val_if_fail (GBP_IS_RECENT_PROJECT_ROW (self), NULL);
+
+ return self->project_info;
+}
+
+static void
+gbp_recent_project_row_create_search_text (GbpRecentProjectRow *self,
+ IdeProjectInfo *project_info)
+{
+ const gchar *tmp;
+ IdeDoap *doap;
+ GString *str;
+ GFile *file;
+
+ g_assert (GBP_IS_RECENT_PROJECT_ROW (self));
+
+ str = g_string_new (NULL);
+
+ if ((tmp = ide_project_info_get_name (project_info)))
+ {
+ g_autofree gchar *downcase = g_utf8_strdown (tmp, -1);
+
+ g_string_append (str, tmp);
+ g_string_append (str, " ");
+ g_string_append (str, downcase);
+ g_string_append (str, " ");
+ }
+
+ if ((tmp = ide_project_info_get_description (project_info)))
+ {
+ g_string_append (str, tmp);
+ g_string_append (str, " ");
+ }
+
+ doap = ide_project_info_get_doap (project_info);
+
+ if (doap != NULL)
+ {
+ if ((tmp = ide_doap_get_description (doap)))
+ {
+ g_string_append (str, tmp);
+ g_string_append (str, " ");
+ }
+ }
+
+ file = ide_project_info_get_file (project_info);
+
+ if (file != NULL)
+ {
+ g_autoptr(GFile) parent = g_file_get_parent (file);
+ g_autofree gchar *dir = parent ? g_file_get_basename (parent) : NULL;
+ g_autofree gchar *base = g_file_get_basename (file);
+
+ if (dir != NULL)
+ {
+ g_string_append (str, dir);
+ g_string_append (str, " ");
+ }
+
+ if (base != NULL)
+ {
+ g_string_append (str, base);
+ g_string_append (str, " ");
+ }
+ }
+
+ g_free (self->search_text);
+ self->search_text = g_strdelimit (g_string_free (str, FALSE), "\n", ' ');
+}
+
+static void
+gbp_recent_project_row_add_tags (GbpRecentProjectRow *self,
+ IdeProjectInfo *project_info)
+{
+ const gchar * const *languages;
+ const gchar *build_system_name;
+
+ g_return_if_fail (GBP_IS_RECENT_PROJECT_ROW (self));
+ g_return_if_fail (IDE_IS_PROJECT_INFO (project_info));
+
+ languages = ide_project_info_get_languages (project_info);
+
+ if (languages != NULL)
+ {
+ guint len = g_strv_length ((gchar **)languages);
+ gsize i;
+
+ for (i = len; i > 0; i--)
+ {
+ const gchar *name = languages [i - 1];
+ GtkWidget *pill;
+
+ pill = g_object_new (DZL_TYPE_PILL_BOX,
+ "visible", TRUE,
+ "label", name,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (self->tags_box), pill);
+ }
+ }
+
+ build_system_name = ide_project_info_get_build_system_name (project_info);
+ if (!ide_str_empty0 (build_system_name))
+ {
+ GtkWidget *pill;
+
+ pill = g_object_new (DZL_TYPE_PILL_BOX,
+ "visible", TRUE,
+ "label", build_system_name,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (self->tags_box), pill);
+ }
+}
+
+static void
+gbp_recent_project_row_set_project_info (GbpRecentProjectRow *self,
+ IdeProjectInfo *project_info)
+{
+ g_return_if_fail (GBP_IS_RECENT_PROJECT_ROW (self));
+ g_return_if_fail (!project_info || IDE_IS_PROJECT_INFO (project_info));
+
+ if (g_set_object (&self->project_info, project_info))
+ {
+ dzl_binding_group_set_source (self->bindings, project_info);
+
+ if (project_info != NULL)
+ {
+ gbp_recent_project_row_add_tags (self, project_info);
+ gbp_recent_project_row_create_search_text (self, project_info);
+ }
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PROJECT_INFO]);
+ }
+}
+
+static gboolean
+humanize_date_time (GBinding *binding,
+ const GValue *from_value,
+ GValue *to_value,
+ gpointer user_data)
+{
+ GDateTime *dt;
+ gchar *str;
+
+ g_assert (G_VALUE_HOLDS (from_value, G_TYPE_DATE_TIME));
+ g_assert (G_VALUE_HOLDS (to_value, G_TYPE_STRING));
+
+ if (!(dt = g_value_get_boxed (from_value)))
+ return FALSE;
+
+ str = dzl_g_date_time_format_for_display (dt);
+ g_value_take_string (to_value, str);
+
+ return TRUE;
+}
+
+static gboolean
+truncate_location (GBinding *binding,
+ const GValue *from_value,
+ GValue *to_value,
+ gpointer user_data)
+{
+ GFile *file;
+ gchar *uri;
+
+ g_assert (G_VALUE_HOLDS (from_value, G_TYPE_FILE));
+ g_assert (G_VALUE_HOLDS (to_value, G_TYPE_STRING));
+
+ if (!(file = g_value_get_object (from_value)))
+ return FALSE;
+
+ if (g_file_is_native (file))
+ {
+ gchar *relative_path;
+
+ if ((relative_path = g_file_get_relative_path (home_dir, file)) ||
+ (relative_path = g_file_get_path (file)))
+ {
+ g_value_take_string (to_value, relative_path);
+ return TRUE;
+ }
+
+ g_free (relative_path);
+ }
+
+ uri = g_file_get_uri (file);
+ g_value_set_string (to_value, uri);
+
+ return TRUE;
+}
+
+const gchar *
+gbp_recent_project_row_get_search_text (GbpRecentProjectRow *self)
+{
+ g_return_val_if_fail (GBP_IS_RECENT_PROJECT_ROW (self), NULL);
+
+ return self->search_text;
+}
+
+static void
+gbp_recent_project_row_finalize (GObject *object)
+{
+ GbpRecentProjectRow *self = (GbpRecentProjectRow *)object;
+
+ g_clear_object (&self->project_info);
+ g_clear_object (&self->bindings);
+ g_clear_pointer (&self->search_text, g_free);
+
+ G_OBJECT_CLASS (gbp_recent_project_row_parent_class)->finalize (object);
+}
+
+static void
+gbp_recent_project_row_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GbpRecentProjectRow *self = GBP_RECENT_PROJECT_ROW (object);
+
+ switch (prop_id)
+ {
+ case PROP_PROJECT_INFO:
+ g_value_set_object (value, gbp_recent_project_row_get_project_info (self));
+ break;
+
+ case PROP_SELECTED:
+ g_object_get_property (G_OBJECT (self->checkbox), "active", value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gbp_recent_project_row_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GbpRecentProjectRow *self = GBP_RECENT_PROJECT_ROW (object);
+
+ switch (prop_id)
+ {
+ case PROP_SELECTED:
+ g_object_set_property (G_OBJECT (self->checkbox), "active", value);
+ break;
+
+ case PROP_SELECTION_MODE:
+ gbp_recent_project_row_set_selection_mode (self, g_value_get_boolean (value));
+ break;
+
+ case PROP_PROJECT_INFO:
+ gbp_recent_project_row_set_project_info (self, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gbp_recent_project_row_class_init (GbpRecentProjectRowClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = gbp_recent_project_row_finalize;
+ object_class->get_property = gbp_recent_project_row_get_property;
+ object_class->set_property = gbp_recent_project_row_set_property;
+
+ gtk_widget_class_set_template_from_resource (widget_class,
+
"/org/gnome/builder/plugins/recent-plugin/gbp-recent-project-row.ui");
+ gtk_widget_class_bind_template_child (widget_class, GbpRecentProjectRow, checkbox);
+ gtk_widget_class_bind_template_child (widget_class, GbpRecentProjectRow, date_label);
+ gtk_widget_class_bind_template_child (widget_class, GbpRecentProjectRow, description_label);
+ gtk_widget_class_bind_template_child (widget_class, GbpRecentProjectRow, location_label);
+ gtk_widget_class_bind_template_child (widget_class, GbpRecentProjectRow, tags_box);
+ gtk_widget_class_bind_template_child (widget_class, GbpRecentProjectRow, title_label);
+
+ properties [PROP_SELECTED] =
+ g_param_spec_boolean ("selected",
+ "Selected",
+ "Selected",
+ FALSE,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_SELECTION_MODE] =
+ g_param_spec_boolean ("selection-mode",
+ "Selection Mode",
+ "Selection Mode",
+ FALSE,
+ (G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_PROJECT_INFO] =
+ g_param_spec_object ("project-info",
+ "Project Information",
+ "The project information to render.",
+ IDE_TYPE_PROJECT_INFO,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, LAST_PROP, properties);
+
+ home_dir = g_file_new_for_path (g_get_home_dir ());
+}
+
+static void
+gbp_recent_project_row_init (GbpRecentProjectRow *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ self->bindings = dzl_binding_group_new ();
+
+ dzl_binding_group_bind (self->bindings, "name", self->title_label, "label", 0);
+ dzl_binding_group_bind_full (self->bindings, "last-modified-at", self->date_label, "label", 0,
+ humanize_date_time, NULL, NULL, NULL);
+ dzl_binding_group_bind_full (self->bindings, "directory", self->location_label, "label", 0,
+ truncate_location, NULL, NULL, NULL);
+ dzl_binding_group_bind (self->bindings, "description", self->description_label, "label", 0);
+
+ g_object_bind_property (self->checkbox, "active", self, "selected", 0);
+}
diff --git a/src/plugins/recent/gbp-recent-project-row.h b/src/plugins/recent/gbp-recent-project-row.h
new file mode 100644
index 0000000..ae06239
--- /dev/null
+++ b/src/plugins/recent/gbp-recent-project-row.h
@@ -0,0 +1,34 @@
+/* ide-greeter-project-row.h
+ *
+ * Copyright © 2015 Christian Hergert <christian hergert me>
+ *
+ * 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/>.
+ */
+
+#pragma once
+
+#include <ide.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_RECENT_PROJECT_ROW (gbp_recent_project_row_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpRecentProjectRow, gbp_recent_project_row, GBP, RECENT_PROJECT_ROW, GtkListBoxRow)
+
+IdeProjectInfo *gbp_recent_project_row_get_project_info (GbpRecentProjectRow *self);
+const gchar *gbp_recent_project_row_get_search_text (GbpRecentProjectRow *self);
+void gbp_recent_project_row_set_selection_mode (GbpRecentProjectRow *self,
+ gboolean selection_mode);
+
+G_END_DECLS
diff --git a/src/plugins/recent/gbp-recent-project-row.ui b/src/plugins/recent/gbp-recent-project-row.ui
new file mode 100644
index 0000000..4f924a3
--- /dev/null
+++ b/src/plugins/recent/gbp-recent-project-row.ui
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.18 -->
+ <template class="GbpRecentProjectRow" parent="GtkListBoxRow">
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">horizontal</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkCheckButton" id="checkbox">
+ <property name="margin-start">12</property>
+ <property name="valign">center</property>
+ <property name="vexpand">true</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <property name="margin">12</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">horizontal</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkLabel" id="title_label">
+ <property name="visible">true</property>
+ <property name="hexpand">true</property>
+ <property name="valign">baseline</property>
+ <property name="xalign">0.0</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="date_label">
+ <property name="visible">true</property>
+ <property name="hexpand">true</property>
+ <property name="valign">baseline</property>
+ <property name="xalign">1.0</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="description_label">
+ <property name="single-line-mode">true</property>
+ <property name="ellipsize">end</property>
+ <property name="valign">baseline</property>
+ <property name="visible">true</property>
+ <property name="xalign">0.0</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">horizontal</property>
+ <property name="visible">true</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="location_label">
+ <property name="hexpand">true</property>
+ <property name="visible">true</property>
+ <property name="valign">baseline</property>
+ <property name="xalign">0.0</property>
+ <property name="ellipsize">middle</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="tags_box">
+ <property name="spacing">3</property>
+ <property name="orientation">horizontal</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/src/plugins/recent/gbp-recent-section.c b/src/plugins/recent/gbp-recent-section.c
new file mode 100644
index 0000000..fca623b
--- /dev/null
+++ b/src/plugins/recent/gbp-recent-section.c
@@ -0,0 +1,212 @@
+/* gbp-recent-section.c
+ *
+ * Copyright (C) 2017 Christian Hergert <chergert redhat 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/>.
+ */
+
+#define G_LOG_DOMAIN "gbp-recent-section"
+
+#include <ide.h>
+
+#include "gbp-recent-project-row.h"
+#include "gbp-recent-section.h"
+
+struct _GbpRecentSection
+{
+ GtkBin parent_instance;
+ GtkListBox *listbox;
+};
+
+static gint
+gbp_recent_section_get_priority (IdeGreeterSection *section)
+{
+ return 0;
+}
+
+static void
+gbp_recent_section_filter_cb (GtkListBoxRow *row,
+ gpointer user_data)
+{
+ struct {
+ DzlPatternSpec *spec;
+ gboolean found;
+ } *filter = user_data;
+ gboolean match;
+
+ g_assert (GBP_IS_RECENT_PROJECT_ROW (row));
+ g_assert (filter != NULL);
+
+ if (filter->spec != NULL)
+ {
+ const gchar *search_text;
+
+ search_text = gbp_recent_project_row_get_search_text (GBP_RECENT_PROJECT_ROW (row));
+ match = dzl_pattern_spec_match (filter->spec, search_text);
+ }
+
+ gtk_widget_set_visible (GTK_WIDGET (row), match);
+
+ filter->found |= match;
+}
+
+static gboolean
+gbp_recent_section_filter (IdeGreeterSection *section,
+ DzlPatternSpec *spec)
+{
+ GbpRecentSection *self = (GbpRecentSection *)section;
+ struct {
+ DzlPatternSpec *spec;
+ gboolean found;
+ } filter = { spec, FALSE };
+
+ g_assert (GBP_IS_RECENT_SECTION (self));
+
+ /* We don't use filter func here so that we know if any
+ * rows matched. We have to hide our widget if there are
+ * no visible matches.
+ */
+
+ gtk_container_foreach (GTK_CONTAINER (self->listbox),
+ (GtkCallback) gbp_recent_section_filter_cb,
+ &filter);
+
+ return filter.found;
+}
+
+static void
+gbp_recent_section_activate_first_cb (GtkWidget *widget,
+ gpointer user_data)
+{
+ IdeProjectInfo *project_info;
+ struct {
+ GbpRecentSection *self;
+ gboolean handled;
+ } *activate = user_data;
+
+ g_assert (GBP_IS_RECENT_PROJECT_ROW (widget));
+ g_assert (activate != NULL);
+
+ if (activate->handled || !gtk_widget_get_visible (widget))
+ return;
+
+ project_info = gbp_recent_project_row_get_project_info (GBP_RECENT_PROJECT_ROW (widget));
+ ide_greeter_section_emit_project_activated (IDE_GREETER_SECTION (activate->self), project_info);
+
+ activate->handled = TRUE;
+}
+
+static gboolean
+gbp_recent_section_activate_first (IdeGreeterSection *section)
+{
+ GbpRecentSection *self = (GbpRecentSection *)section;
+ struct {
+ GbpRecentSection *self;
+ gboolean handled;
+ } activate;
+
+ g_return_val_if_fail (GBP_IS_RECENT_SECTION (self), FALSE);
+
+ activate.self = self;
+ activate.handled = FALSE;
+
+ gtk_container_foreach (GTK_CONTAINER (self->listbox),
+ gbp_recent_section_activate_first_cb,
+ &activate);
+
+ return activate.handled;
+}
+
+static void
+greeter_section_iface_init (IdeGreeterSectionInterface *iface)
+{
+ iface->get_priority = gbp_recent_section_get_priority;
+ iface->filter = gbp_recent_section_filter;
+ iface->activate_first = gbp_recent_section_activate_first;
+}
+
+G_DEFINE_TYPE_WITH_CODE (GbpRecentSection, gbp_recent_section, GTK_TYPE_BIN,
+ G_IMPLEMENT_INTERFACE (IDE_TYPE_GREETER_SECTION,
+ greeter_section_iface_init))
+
+static void
+gbp_recent_section_row_activated (GbpRecentSection *self,
+ GbpRecentProjectRow *row,
+ GtkListBox *list_box)
+{
+ IdeProjectInfo *project_info;
+
+ g_assert (GBP_IS_RECENT_SECTION (self));
+ g_assert (GBP_IS_RECENT_PROJECT_ROW (row));
+ g_assert (GTK_IS_LIST_BOX (list_box));
+
+ project_info = gbp_recent_project_row_get_project_info (row);
+
+ ide_greeter_section_emit_project_activated (IDE_GREETER_SECTION (self), project_info);
+}
+
+static GtkWidget *
+create_widget_func (gpointer item,
+ gpointer user_data)
+{
+ IdeProjectInfo *project_info = item;
+ GbpRecentProjectRow *row;
+
+ g_assert (IDE_IS_PROJECT_INFO (project_info));
+
+ row = g_object_new (GBP_TYPE_RECENT_PROJECT_ROW,
+ "project-info", project_info,
+ "visible", TRUE,
+ NULL);
+
+ return GTK_WIDGET (row);
+}
+
+static void
+gbp_recent_section_constructed (GObject *object)
+{
+ GbpRecentSection *self = (GbpRecentSection *)object;
+ IdeRecentProjects *projects;
+
+ G_OBJECT_CLASS (gbp_recent_section_parent_class)->constructed (object);
+
+ projects = ide_application_get_recent_projects (IDE_APPLICATION_DEFAULT);
+
+ gtk_list_box_bind_model (self->listbox,
+ G_LIST_MODEL (projects),
+ create_widget_func, self, NULL);
+}
+
+static void
+gbp_recent_section_class_init (GbpRecentSectionClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->constructed = gbp_recent_section_constructed;
+
+ gtk_widget_class_set_css_name (widget_class, "recent");
+ gtk_widget_class_set_template_from_resource (widget_class,
+
"/org/gnome/builder/plugins/recent-plugin/gbp-recent-section.ui");
+ gtk_widget_class_bind_template_child (widget_class, GbpRecentSection, listbox);
+ gtk_widget_class_bind_template_callback (widget_class, gbp_recent_section_row_activated);
+
+ g_type_ensure (GBP_TYPE_RECENT_PROJECT_ROW);
+}
+
+static void
+gbp_recent_section_init (GbpRecentSection *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+}
diff --git a/src/plugins/recent/gbp-recent-section.h b/src/plugins/recent/gbp-recent-section.h
new file mode 100644
index 0000000..63a0593
--- /dev/null
+++ b/src/plugins/recent/gbp-recent-section.h
@@ -0,0 +1,29 @@
+/* gbp-recent-section.h
+ *
+ * Copyright (C) 2017 Christian Hergert <chergert redhat 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/>.
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_RECENT_SECTION (gbp_recent_section_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpRecentSection, gbp_recent_section, GBP, RECENT_SECTION, GtkBin)
+
+G_END_DECLS
diff --git a/src/plugins/recent/gbp-recent-section.ui b/src/plugins/recent/gbp-recent-section.ui
new file mode 100644
index 0000000..bbe07d2
--- /dev/null
+++ b/src/plugins/recent/gbp-recent-section.ui
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="GbpRecentSection" parent="GtkBin">
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="DzlBox">
+ <property name="halign">center</property>
+ <property name="hexpand">false</property>
+ <property name="orientation">vertical</property>
+ <property name="visible">true</property>
+ <property name="max-width-request">600</property>
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="label" translatable="yes">Recent Projects</property>
+ <property name="visible">true</property>
+ <property name="xalign">0.0</property>
+ <property name="hexpand">true</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">true</property>
+ <property name="label" translatable="yes">Updated</property>
+ <property name="margin-end">10</property>
+ <property name="halign">end</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkFrame">
+ <property name="halign">center</property>
+ <property name="visible">true</property>
+ <property name="width-request">600</property>
+ <child>
+ <object class="GtkListBox" id="listbox">
+ <property name="visible">true</property>
+ <property name="selection-mode">none</property>
+ <signal name="row-activated" swapped="true" object="GbpRecentSection"
handler="gbp_recent_section_row_activated"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/src/plugins/recent/meson.build b/src/plugins/recent/meson.build
new file mode 100644
index 0000000..e8501df
--- /dev/null
+++ b/src/plugins/recent/meson.build
@@ -0,0 +1,16 @@
+recent_resources = gnome.compile_resources(
+ 'recent-resources',
+ 'recent.gresource.xml',
+ c_name: 'gbp_recent',
+)
+
+recent_sources = [
+ 'recent-plugin.c',
+ 'gbp-recent-project-row.c',
+ 'gbp-recent-project-row.h',
+ 'gbp-recent-section.c',
+ 'gbp-recent-section.h',
+]
+
+gnome_builder_plugins_sources += files(recent_sources)
+gnome_builder_plugins_sources += recent_resources[0]
diff --git a/src/plugins/recent/recent-plugin.c b/src/plugins/recent/recent-plugin.c
new file mode 100644
index 0000000..6c669da
--- /dev/null
+++ b/src/plugins/recent/recent-plugin.c
@@ -0,0 +1,30 @@
+/* recent-plugin.c
+ *
+ * Copyright (C) 2017 Christian Hergert <chergert redhat 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/>.
+ */
+
+#include <libpeas/peas.h>
+#include <ide.h>
+
+#include "gbp-recent-section.h"
+
+void
+gbp_recent_register_types (PeasObjectModule *module)
+{
+ peas_object_module_register_extension_type (module,
+ IDE_TYPE_GREETER_SECTION,
+ GBP_TYPE_RECENT_SECTION);
+}
diff --git a/src/plugins/recent/recent.gresource.xml b/src/plugins/recent/recent.gresource.xml
new file mode 100644
index 0000000..587e695
--- /dev/null
+++ b/src/plugins/recent/recent.gresource.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/builder/plugins">
+ <file>recent.plugin</file>
+ </gresource>
+ <gresource prefix="/org/gnome/builder/plugins/recent-plugin">
+ <file>gbp-recent-project-row.ui</file>
+ <file>gbp-recent-section.ui</file>
+ </gresource>
+</gresources>
diff --git a/src/plugins/recent/recent.plugin b/src/plugins/recent/recent.plugin
new file mode 100644
index 0000000..d5c3385
--- /dev/null
+++ b/src/plugins/recent/recent.plugin
@@ -0,0 +1,9 @@
+[Plugin]
+Module=recent-plugin
+Name=Recent Projects
+Description=Shows recent projects in the greeter
+Authors=Christian Hergert <christian hergert me>
+Copyright=Copyright © 2017 Christian Hergert
+Builtin=true
+Hidden=true
+Embedded=gbp_recent_register_types
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]