[gnome-builder: 111/139] project-tree: rewrite project tree using libide-tree
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder: 111/139] project-tree: rewrite project tree using libide-tree
- Date: Thu, 10 Jan 2019 04:26:39 +0000 (UTC)
commit 91845ac3f45d2ab498d48148e052dbfe2a476a33
Author: Christian Hergert <chergert redhat com>
Date: Wed Jan 9 17:34:02 2019 -0800
project-tree: rewrite project tree using libide-tree
This provides a new implementation of the project-tree using updated
designs from libide including libide-tree. Much of the code involving
popovers has been simplified in terms of async tracking. Some features
have been broken out into other plugins.
src/plugins/project-tree/gb-new-file-popover.h | 38 -
src/plugins/project-tree/gb-project-file.c | 302 ------
src/plugins/project-tree/gb-project-file.h | 48 -
src/plugins/project-tree/gb-project-tree-actions.c | 1004 --------------------
src/plugins/project-tree/gb-project-tree-addin.c | 131 ---
src/plugins/project-tree/gb-project-tree-builder.c | 950 ------------------
src/plugins/project-tree/gb-project-tree-builder.h | 33 -
.../project-tree/gb-project-tree-editor-addin.c | 121 ---
.../project-tree/gb-project-tree-shortcuts.c | 74 --
src/plugins/project-tree/gb-project-tree.c | 631 ------------
src/plugins/project-tree/gb-project-tree.h | 46 -
src/plugins/project-tree/gb-rename-file-popover.h | 34 -
src/plugins/project-tree/gb-vcs-tree-builder.c | 177 ----
...b-new-file-popover.c => gbp-new-file-popover.c} | 227 +++--
src/plugins/project-tree/gbp-new-file-popover.h | 48 +
...new-file-popover.ui => gbp-new-file-popover.ui} | 2 +-
src/plugins/project-tree/gbp-project-tree-addin.c | 896 +++++++++++++++++
...oject-tree-addin.h => gbp-project-tree-addin.h} | 8 +-
.../project-tree/gbp-project-tree-pane-actions.c | 634 ++++++++++++
src/plugins/project-tree/gbp-project-tree-pane.c | 62 ++
...tree-editor-addin.h => gbp-project-tree-pane.h} | 10 +-
src/plugins/project-tree/gbp-project-tree-pane.ui | 20 +
...t-tree-private.h => gbp-project-tree-private.h} | 24 +-
.../gbp-project-tree-workspace-addin.c | 102 ++
...uilder.h => gbp-project-tree-workspace-addin.h} | 12 +-
src/plugins/project-tree/gbp-project-tree.c | 178 ++++
...b-project-tree-actions.h => gbp-project-tree.h} | 11 +-
...me-file-popover.c => gbp-rename-file-popover.c} | 178 +++-
src/plugins/project-tree/gbp-rename-file-popover.h | 42 +
...-file-popover.ui => gbp-rename-file-popover.ui} | 2 +-
src/plugins/project-tree/gtk/menus.ui | 108 +--
src/plugins/project-tree/meson.build | 49 +-
src/plugins/project-tree/project-tree-plugin.c | 21 +-
.../project-tree/project-tree.gresource.xml | 11 +-
src/plugins/project-tree/project-tree.plugin | 14 +-
src/plugins/project-tree/themes/shared.css | 8 +
36 files changed, 2362 insertions(+), 3894 deletions(-)
---
diff --git a/src/plugins/project-tree/gb-new-file-popover.c b/src/plugins/project-tree/gbp-new-file-popover.c
similarity index 51%
rename from src/plugins/project-tree/gb-new-file-popover.c
rename to src/plugins/project-tree/gbp-new-file-popover.c
index f71220977..fa24efc17 100644
--- a/src/plugins/project-tree/gb-new-file-popover.c
+++ b/src/plugins/project-tree/gbp-new-file-popover.c
@@ -1,4 +1,4 @@
-/* gb-new-file-popover.c
+/* gbp-new-file-popover.c
*
* Copyright 2015-2019 Christian Hergert <christian hergert me>
*
@@ -18,18 +18,21 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
+#define G_LOG_DOMAIN "gbp-new-file-popover"
+
#include <glib/gi18n.h>
-#include <ide.h>
+#include <libide-gui.h>
+#include <libide-threading.h>
-#include "gb-new-file-popover.h"
+#include "gbp-new-file-popover.h"
-struct _GbNewFilePopover
+struct _GbpNewFilePopover
{
GtkPopover parent_instance;
GFileType file_type;
GFile *directory;
- GCancellable *cancellable;
+ IdeTask *task;
GtkButton *button;
GtkEntry *entry;
@@ -37,31 +40,26 @@ struct _GbNewFilePopover
GtkLabel *title;
};
-G_DEFINE_TYPE (GbNewFilePopover, gb_new_file_popover, GTK_TYPE_POPOVER)
+G_DEFINE_TYPE (GbpNewFilePopover, gbp_new_file_popover, GTK_TYPE_POPOVER)
enum {
PROP_0,
PROP_DIRECTORY,
PROP_FILE_TYPE,
- LAST_PROP
-};
-
-enum {
- CREATE_FILE,
- LAST_SIGNAL
+ N_PROPS
};
-static GParamSpec *properties [LAST_PROP];
-static guint signals [LAST_SIGNAL];
+static GParamSpec *properties [N_PROPS];
static void
-gb_new_file_popover__button_clicked (GbNewFilePopover *self,
+gbp_new_file_popover_button_clicked (GbpNewFilePopover *self,
GtkButton *button)
{
g_autoptr(GFile) file = NULL;
+ g_autoptr(IdeTask) task = NULL;
const gchar *path;
- g_assert (GB_IS_NEW_FILE_POPOVER (self));
+ g_assert (GBP_IS_NEW_FILE_POPOVER (self));
g_assert (GTK_IS_BUTTON (button));
if (self->directory == NULL)
@@ -73,14 +71,15 @@ gb_new_file_popover__button_clicked (GbNewFilePopover *self,
file = g_file_get_child (self->directory, path);
- g_signal_emit (self, signals [CREATE_FILE], 0, file, self->file_type);
+ if ((task = g_steal_pointer (&self->task)))
+ ide_task_return_pointer (task, g_steal_pointer (&file), g_object_unref);
}
static void
-gb_new_file_popover__entry_activate (GbNewFilePopover *self,
+gbp_new_file_popover_entry_activate (GbpNewFilePopover *self,
GtkEntry *entry)
{
- g_assert (GB_IS_NEW_FILE_POPOVER (self));
+ g_assert (GBP_IS_NEW_FILE_POPOVER (self));
g_assert (GTK_IS_ENTRY (entry));
if (gtk_widget_get_sensitive (GTK_WIDGET (self->button)))
@@ -88,13 +87,13 @@ gb_new_file_popover__entry_activate (GbNewFilePopover *self,
}
static void
-gb_new_file_popover__query_info_cb (GObject *object,
+gbp_new_file_popover_query_info_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
GFile *file = (GFile *)object;
g_autoptr(GFileInfo) file_info = NULL;
- g_autoptr(GbNewFilePopover) self = user_data;
+ g_autoptr(GbpNewFilePopover) self = user_data;
g_autoptr(GError) error = NULL;
GFileType file_type;
@@ -131,91 +130,99 @@ gb_new_file_popover__query_info_cb (GObject *object,
}
static void
-gb_new_file_popover_check_exists (GbNewFilePopover *self,
- GFile *directory,
- const gchar *path)
+gbp_new_file_popover_check_exists (GbpNewFilePopover *self,
+ GFile *directory,
+ const gchar *path)
{
g_autoptr(GFile) child = NULL;
+ GCancellable *cancellable = NULL;
- g_assert (GB_IS_NEW_FILE_POPOVER (self));
+ g_assert (GBP_IS_NEW_FILE_POPOVER (self));
g_assert (!directory || G_IS_FILE (directory));
- if (self->cancellable != NULL)
- {
- if (!g_cancellable_is_cancelled (self->cancellable))
- g_cancellable_cancel (self->cancellable);
- g_clear_object (&self->cancellable);
- }
-
gtk_label_set_label (self->message, NULL);
gtk_widget_set_sensitive (GTK_WIDGET (self->button), FALSE);
if (directory == NULL)
return;
- if (dzl_str_empty0 (path))
+ if (ide_str_empty0 (path))
return;
child = g_file_get_child (directory, path);
- self->cancellable = g_cancellable_new ();
+ if (self->task)
+ cancellable = ide_task_get_cancellable (self->task);
g_file_query_info_async (child,
G_FILE_ATTRIBUTE_STANDARD_TYPE,
G_FILE_QUERY_INFO_NONE,
G_PRIORITY_DEFAULT,
- self->cancellable,
- gb_new_file_popover__query_info_cb,
+ cancellable,
+ gbp_new_file_popover_query_info_cb,
g_object_ref (self));
}
static void
-gb_new_file_popover__entry_changed (GbNewFilePopover *self,
+gbp_new_file_popover_entry_changed (GbpNewFilePopover *self,
GtkEntry *entry)
{
const gchar *text;
- g_assert (GB_IS_NEW_FILE_POPOVER (self));
+ g_assert (GBP_IS_NEW_FILE_POPOVER (self));
g_assert (GTK_IS_ENTRY (entry));
text = gtk_entry_get_text (entry);
gtk_widget_set_sensitive (GTK_WIDGET (self->button), !dzl_str_empty0 (text));
- gb_new_file_popover_check_exists (self, self->directory, text);
+ gbp_new_file_popover_check_exists (self, self->directory, text);
+}
+
+static void
+gbp_new_file_popover_closed (GtkPopover *popover)
+{
+ GbpNewFilePopover *self = (GbpNewFilePopover *)popover;
+ g_autoptr(IdeTask) task = NULL;
+
+ g_assert (GBP_IS_NEW_FILE_POPOVER (self));
+
+ if ((task = g_steal_pointer (&self->task)))
+ ide_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_CANCELLED,
+ "The popover was closed");
}
static void
-gb_new_file_popover_finalize (GObject *object)
+gbp_new_file_popover_finalize (GObject *object)
{
- GbNewFilePopover *self = (GbNewFilePopover *)object;
+ GbpNewFilePopover *self = (GbpNewFilePopover *)object;
- if (self->cancellable && !g_cancellable_is_cancelled (self->cancellable))
- g_cancellable_cancel (self->cancellable);
+ g_assert (self->task == NULL);
- g_clear_object (&self->cancellable);
g_clear_object (&self->directory);
- G_OBJECT_CLASS (gb_new_file_popover_parent_class)->finalize (object);
+ G_OBJECT_CLASS (gbp_new_file_popover_parent_class)->finalize (object);
}
static void
-gb_new_file_popover_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
+gbp_new_file_popover_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
{
- GbNewFilePopover *self = GB_NEW_FILE_POPOVER(object);
+ GbpNewFilePopover *self = GBP_NEW_FILE_POPOVER(object);
switch (prop_id)
{
case PROP_DIRECTORY:
- g_value_set_object (value, gb_new_file_popover_get_directory (self));
+ g_value_set_object (value, gbp_new_file_popover_get_directory (self));
break;
case PROP_FILE_TYPE:
- g_value_set_enum (value, gb_new_file_popover_get_file_type (self));
+ g_value_set_enum (value, gbp_new_file_popover_get_file_type (self));
break;
default:
@@ -224,7 +231,7 @@ gb_new_file_popover_get_property (GObject *object,
}
/**
- * gb_new_file_popover_set_property:
+ * gbp_new_file_popover_set_property:
* @object: (in): a #GObject.
* @prop_id: (in): The property identifier.
* @value: (in): The given property.
@@ -235,21 +242,21 @@ gb_new_file_popover_get_property (GObject *object,
* Since: 3.32
*/
static void
-gb_new_file_popover_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
+gbp_new_file_popover_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
{
- GbNewFilePopover *self = GB_NEW_FILE_POPOVER(object);
+ GbpNewFilePopover *self = GBP_NEW_FILE_POPOVER(object);
switch (prop_id)
{
case PROP_DIRECTORY:
- gb_new_file_popover_set_directory (self, g_value_get_object (value));
+ gbp_new_file_popover_set_directory (self, g_value_get_object (value));
break;
case PROP_FILE_TYPE:
- gb_new_file_popover_set_file_type (self, g_value_get_enum (value));
+ gbp_new_file_popover_set_file_type (self, g_value_get_enum (value));
break;
default:
@@ -258,14 +265,17 @@ gb_new_file_popover_set_property (GObject *object,
}
static void
-gb_new_file_popover_class_init (GbNewFilePopoverClass *klass)
+gbp_new_file_popover_class_init (GbpNewFilePopoverClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GtkPopoverClass *popover_class = GTK_POPOVER_CLASS (klass);
- object_class->finalize = gb_new_file_popover_finalize;
- object_class->get_property = gb_new_file_popover_get_property;
- object_class->set_property = gb_new_file_popover_set_property;
+ object_class->finalize = gbp_new_file_popover_finalize;
+ object_class->get_property = gbp_new_file_popover_get_property;
+ object_class->set_property = gbp_new_file_popover_set_property;
+
+ popover_class->closed = gbp_new_file_popover_closed;
properties [PROP_DIRECTORY] =
g_param_spec_object ("directory",
@@ -282,28 +292,17 @@ gb_new_file_popover_class_init (GbNewFilePopoverClass *klass)
G_FILE_TYPE_REGULAR,
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- g_object_class_install_properties (object_class, LAST_PROP, properties);
-
- signals [CREATE_FILE] =
- g_signal_new ("create-file",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_FIRST,
- 0,
- NULL, NULL, NULL,
- G_TYPE_NONE,
- 2,
- G_TYPE_FILE,
- G_TYPE_FILE_TYPE);
-
- gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/builder/plugins/project-tree-plugin/gb-new-file-popover.ui");
- gtk_widget_class_bind_template_child (widget_class, GbNewFilePopover, button);
- gtk_widget_class_bind_template_child (widget_class, GbNewFilePopover, entry);
- gtk_widget_class_bind_template_child (widget_class, GbNewFilePopover, message);
- gtk_widget_class_bind_template_child (widget_class, GbNewFilePopover, title);
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+
+ gtk_widget_class_set_template_from_resource (widget_class,
"/plugins/project-tree/gbp-new-file-popover.ui");
+ gtk_widget_class_bind_template_child (widget_class, GbpNewFilePopover, button);
+ gtk_widget_class_bind_template_child (widget_class, GbpNewFilePopover, entry);
+ gtk_widget_class_bind_template_child (widget_class, GbpNewFilePopover, message);
+ gtk_widget_class_bind_template_child (widget_class, GbpNewFilePopover, title);
}
static void
-gb_new_file_popover_init (GbNewFilePopover *self)
+gbp_new_file_popover_init (GbpNewFilePopover *self)
{
self->file_type = G_FILE_TYPE_REGULAR;
@@ -311,36 +310,36 @@ gb_new_file_popover_init (GbNewFilePopover *self)
g_signal_connect_object (self->entry,
"activate",
- G_CALLBACK (gb_new_file_popover__entry_activate),
+ G_CALLBACK (gbp_new_file_popover_entry_activate),
self,
G_CONNECT_SWAPPED);
g_signal_connect_object (self->entry,
"changed",
- G_CALLBACK (gb_new_file_popover__entry_changed),
+ G_CALLBACK (gbp_new_file_popover_entry_changed),
self,
G_CONNECT_SWAPPED);
g_signal_connect_object (self->button,
"clicked",
- G_CALLBACK (gb_new_file_popover__button_clicked),
+ G_CALLBACK (gbp_new_file_popover_button_clicked),
self,
G_CONNECT_SWAPPED);
}
GFileType
-gb_new_file_popover_get_file_type (GbNewFilePopover *self)
+gbp_new_file_popover_get_file_type (GbpNewFilePopover *self)
{
- g_return_val_if_fail (GB_IS_NEW_FILE_POPOVER (self), 0);
+ g_return_val_if_fail (GBP_IS_NEW_FILE_POPOVER (self), 0);
return self->file_type;
}
void
-gb_new_file_popover_set_file_type (GbNewFilePopover *self,
- GFileType file_type)
+gbp_new_file_popover_set_file_type (GbpNewFilePopover *self,
+ GFileType file_type)
{
- g_return_if_fail (GB_IS_NEW_FILE_POPOVER (self));
+ g_return_if_fail (GBP_IS_NEW_FILE_POPOVER (self));
g_return_if_fail ((file_type == G_FILE_TYPE_REGULAR) ||
(file_type == G_FILE_TYPE_DIRECTORY));
@@ -358,10 +357,10 @@ gb_new_file_popover_set_file_type (GbNewFilePopover *self,
}
void
-gb_new_file_popover_set_directory (GbNewFilePopover *self,
- GFile *directory)
+gbp_new_file_popover_set_directory (GbpNewFilePopover *self,
+ GFile *directory)
{
- g_return_if_fail (GB_IS_NEW_FILE_POPOVER (self));
+ g_return_if_fail (GBP_IS_NEW_FILE_POPOVER (self));
g_return_if_fail (G_IS_FILE (directory));
if (g_set_object (&self->directory, directory))
@@ -369,22 +368,54 @@ gb_new_file_popover_set_directory (GbNewFilePopover *self,
const gchar *path;
path = gtk_entry_get_text (self->entry);
- gb_new_file_popover_check_exists (self, directory, path);
+ gbp_new_file_popover_check_exists (self, directory, path);
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DIRECTORY]);
}
}
/**
- * gb_new_file_popover_get_directory:
+ * gbp_new_file_popover_get_directory:
*
* Returns: (transfer none) (nullable): a #GFile or %NULL.
*
* Since: 3.32
*/
GFile *
-gb_new_file_popover_get_directory (GbNewFilePopover *self)
+gbp_new_file_popover_get_directory (GbpNewFilePopover *self)
{
- g_return_val_if_fail (GB_IS_NEW_FILE_POPOVER (self), NULL);
+ g_return_val_if_fail (GBP_IS_NEW_FILE_POPOVER (self), NULL);
return self->directory;
}
+
+void
+gbp_new_file_popover_display_async (GbpNewFilePopover *self,
+ IdeTree *tree,
+ IdeTreeNode *node,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (GBP_IS_NEW_FILE_POPOVER (self));
+ g_return_if_fail (IDE_IS_TREE (tree));
+ g_return_if_fail (IDE_IS_TREE_NODE (node));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+ g_return_if_fail (self->task == NULL);
+
+ self->task = ide_task_new (self, cancellable, callback, user_data);
+ ide_task_set_source_tag (self->task, gbp_new_file_popover_display_async);
+
+ ide_tree_expand_node (tree, node);
+ ide_tree_show_popover_at_node (tree, node, GTK_POPOVER (self));
+}
+
+GFile *
+gbp_new_file_popover_display_finish (GbpNewFilePopover *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (GBP_IS_NEW_FILE_POPOVER (self), NULL);
+ g_return_val_if_fail (IDE_IS_TASK (result), NULL);
+
+ return ide_task_propagate_pointer (IDE_TASK (result), error);
+}
diff --git a/src/plugins/project-tree/gbp-new-file-popover.h b/src/plugins/project-tree/gbp-new-file-popover.h
new file mode 100644
index 000000000..1a7b51aff
--- /dev/null
+++ b/src/plugins/project-tree/gbp-new-file-popover.h
@@ -0,0 +1,48 @@
+/* gbp-new-file-popover.h
+ *
+ * Copyright 2015-2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+#include <libide-tree.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_NEW_FILE_POPOVER (gbp_new_file_popover_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpNewFilePopover, gbp_new_file_popover, GBP, NEW_FILE_POPOVER, GtkPopover)
+
+GFileType gbp_new_file_popover_get_file_type (GbpNewFilePopover *self);
+void gbp_new_file_popover_set_file_type (GbpNewFilePopover *self,
+ GFileType file_type);
+void gbp_new_file_popover_set_directory (GbpNewFilePopover *self,
+ GFile *directory);
+GFile *gbp_new_file_popover_get_directory (GbpNewFilePopover *self);
+void gbp_new_file_popover_display_async (GbpNewFilePopover *self,
+ IdeTree *tree,
+ IdeTreeNode *node,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GFile *gbp_new_file_popover_display_finish (GbpNewFilePopover *self,
+ GAsyncResult *result,
+ GError **error);
+
+G_END_DECLS
diff --git a/src/plugins/project-tree/gb-new-file-popover.ui
b/src/plugins/project-tree/gbp-new-file-popover.ui
similarity index 97%
rename from src/plugins/project-tree/gb-new-file-popover.ui
rename to src/plugins/project-tree/gbp-new-file-popover.ui
index 6e690578b..5cbce4915 100644
--- a/src/plugins/project-tree/gb-new-file-popover.ui
+++ b/src/plugins/project-tree/gbp-new-file-popover.ui
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<!-- interface-requires gtk+ 3.16 -->
- <template class="GbNewFilePopover" parent="GtkPopover">
+ <template class="GbpNewFilePopover" parent="GtkPopover">
<child>
<object class="GtkBox">
<property name="border-width">12</property>
diff --git a/src/plugins/project-tree/gbp-project-tree-addin.c
b/src/plugins/project-tree/gbp-project-tree-addin.c
new file mode 100644
index 000000000..d3d0a7f48
--- /dev/null
+++ b/src/plugins/project-tree/gbp-project-tree-addin.c
@@ -0,0 +1,896 @@
+/* gbp-project-tree-addin.c
+ *
+ * Copyright 2018-2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "gbp-project-tree-addin"
+
+#include "config.h"
+
+#include <dazzle.h>
+#include <glib/gi18n.h>
+#include <libide-gui.h>
+#include <libide-projects.h>
+#include <libide-tree.h>
+#include <libide-vcs.h>
+
+#include "gbp-project-tree-addin.h"
+
+struct _GbpProjectTreeAddin
+{
+ GObject parent_instance;
+
+ IdeTree *tree;
+ IdeTreeModel *model;
+ GSettings *settings;
+
+ guint sort_directories_first : 1;
+ guint show_ignored_files : 1;
+};
+
+typedef struct
+{
+ GFile *file;
+ IdeTreeNode *node;
+} FindFileNode;
+
+static gboolean
+project_file_is_ignored (IdeProjectFile *project_file,
+ IdeVcs *vcs)
+{
+ g_autoptr(GFile) file = NULL;
+
+ g_assert (IDE_IS_PROJECT_FILE (project_file));
+
+ file = ide_project_file_ref_file (project_file);
+
+ return ide_vcs_is_ignored (vcs, file, NULL);
+}
+
+static gint
+compare_files (gconstpointer a,
+ gconstpointer b,
+ gpointer user_data)
+{
+ GbpProjectTreeAddin *self = user_data;
+ IdeProjectFile *file_a = *(IdeProjectFile **)a;
+ IdeProjectFile *file_b = *(IdeProjectFile **)b;
+
+ g_assert (IDE_IS_PROJECT_FILE (file_a));
+ g_assert (IDE_IS_PROJECT_FILE (file_b));
+
+ if (self->sort_directories_first)
+ return ide_project_file_compare_directories_first (file_a, file_b);
+ else
+ return ide_project_file_compare (file_a, file_b);
+}
+
+static IdeTreeNode *
+create_file_node (IdeProjectFile *file)
+{
+ IdeTreeNode *child;
+
+ g_assert (IDE_IS_PROJECT_FILE (file));
+
+ child = ide_tree_node_new ();
+ ide_tree_node_set_item (child, G_OBJECT (file));
+ ide_tree_node_set_display_name (child, ide_project_file_get_display_name (file));
+ ide_tree_node_set_icon (child, ide_project_file_get_symbolic_icon (file));
+ g_object_set (child, "destroy-item", TRUE, NULL);
+
+ if (ide_project_file_is_directory (file))
+ {
+ ide_tree_node_set_children_possible (child, TRUE);
+ ide_tree_node_set_expanded_icon_name (child, "folder-open-symbolic");
+ }
+
+ return g_steal_pointer (&child);
+}
+
+static void
+gbp_project_tree_addin_file_list_children_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeProjectFile *project_file = (IdeProjectFile *)object;
+ g_autoptr(IdeTask) task = user_data;
+ g_autoptr(GPtrArray) children = NULL;
+ g_autoptr(GError) error = NULL;
+ GbpProjectTreeAddin *self;
+ IdeTreeNode *last = NULL;
+ IdeTreeNode *node;
+ IdeTreeNode *root;
+ IdeContext *context;
+ IdeVcs *vcs;
+
+ g_assert (IDE_IS_PROJECT_FILE (project_file));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_TASK (task));
+
+ if (!(children = ide_project_file_list_children_finish (project_file, result, &error)))
+ {
+ ide_task_return_error (task, g_steal_pointer (&error));
+ return;
+ }
+
+ IDE_PTR_ARRAY_SET_FREE_FUNC (children, g_object_unref);
+
+ self = ide_task_get_source_object (task);
+ node = ide_task_get_task_data (task);
+ root = ide_tree_node_get_root (node);
+ context = ide_tree_node_get_item (root);
+ vcs = ide_vcs_from_context (context);
+
+ g_assert (GBP_IS_PROJECT_TREE_ADDIN (self));
+ g_assert (IDE_IS_TREE_NODE (node));
+
+ g_ptr_array_sort_with_data (children, compare_files, self);
+
+ for (guint i = 0; i < children->len; i++)
+ {
+ IdeProjectFile *file = g_ptr_array_index (children, i);
+ g_autoptr(IdeTreeNode) child = NULL;
+
+ if (!self->show_ignored_files)
+ {
+ if (project_file_is_ignored (file, vcs))
+ continue;
+ }
+
+ child = create_file_node (file);
+
+ if (last == NULL)
+ ide_tree_node_append (node, child);
+ else
+ ide_tree_node_insert_after (last, child);
+
+ last = child;
+ }
+
+ ide_task_return_boolean (task, TRUE);
+}
+
+static void
+gbp_project_tree_addin_build_children_async (IdeTreeAddin *addin,
+ IdeTreeNode *node,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(IdeTask) task = NULL;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_TREE_ADDIN (addin));
+ g_assert (IDE_IS_TREE_NODE (node));
+
+ task = ide_task_new (addin, cancellable, callback, user_data);
+ ide_task_set_source_tag (task, gbp_project_tree_addin_build_children_async);
+ ide_task_set_task_data (task, g_object_ref (node), g_object_unref);
+
+ if (ide_tree_node_holds (node, IDE_TYPE_CONTEXT))
+ {
+ IdeContext *context = ide_tree_node_get_item (node);
+ g_autoptr(IdeTreeNode) files = NULL;
+ g_autoptr(IdeTreeNode) targets = NULL;
+ //g_autoptr(IdeTreeNode) tests = NULL;
+ g_autoptr(IdeProjectFile) root_file = NULL;
+ g_autoptr(GFile) workdir = ide_context_ref_workdir (context);
+ g_autoptr(GFile) parent = g_file_get_parent (workdir);
+ g_autoptr(GFileInfo) info = NULL;
+ g_autofree gchar *name = NULL;
+
+#if 0
+ tests = g_object_new (IDE_TYPE_TREE_NODE,
+ "icon-name", "builder-unit-tests-symbolic",
+ "item", NULL,
+ "display-name", _("Unit Tests"),
+ "children-possible", TRUE,
+ NULL);
+ ide_tree_node_append (node, tests);
+#endif
+
+ info = g_file_info_new ();
+ name = g_file_get_basename (workdir);
+ g_file_info_set_name (info, name);
+ g_file_info_set_display_name (info, name);
+ g_file_info_set_content_type (info, "inode/directory");
+ g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
+ g_file_info_set_is_symlink (info, FALSE);
+ root_file = ide_project_file_new (parent, info);
+ files = create_file_node (root_file);
+ ide_tree_node_set_display_name (files, _("Files"));
+ ide_tree_node_set_icon_name (files, "view-list-symbolic");
+ ide_tree_node_set_expanded_icon_name (files, "view-list-symbolic");
+ ide_tree_node_append (node, files);
+ }
+ else if (ide_tree_node_holds (node, IDE_TYPE_PROJECT_FILE))
+ {
+ IdeProjectFile *project_file = ide_tree_node_get_item (node);
+
+ ide_project_file_list_children_async (project_file,
+ cancellable,
+ gbp_project_tree_addin_file_list_children_cb,
+ g_steal_pointer (&task));
+
+ return;
+ }
+
+ ide_task_return_boolean (task, TRUE);
+}
+
+static gboolean
+gbp_project_tree_addin_build_children_finish (IdeTreeAddin *addin,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_TREE_ADDIN (addin));
+ g_assert (IDE_IS_TASK (result));
+
+ return ide_task_propagate_boolean (IDE_TASK (result), error);
+}
+
+static gboolean
+gbp_project_tree_addin_node_activated (IdeTreeAddin *addin,
+ IdeTree *tree,
+ IdeTreeNode *node)
+{
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (GBP_IS_PROJECT_TREE_ADDIN (addin));
+ g_assert (IDE_IS_TREE_NODE (node));
+
+ if (ide_tree_node_holds (node, IDE_TYPE_PROJECT_FILE))
+ {
+ IdeProjectFile *project_file = ide_tree_node_get_item (node);
+ g_autoptr(GFile) file = NULL;
+ IdeWorkbench *workbench;
+
+ /* Ignore directories, we want to expand them */
+ if (ide_project_file_is_directory (project_file))
+ return FALSE;
+
+ file = ide_project_file_ref_file (project_file);
+ workbench = ide_widget_get_workbench (GTK_WIDGET (tree));
+
+ ide_workbench_open_async (workbench, file, NULL, 0, NULL, NULL, NULL);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static IdeTreeNodeVisit
+traverse_cb (IdeTreeNode *node,
+ gpointer user_data)
+{
+ FindFileNode *find = user_data;
+
+ if (ide_tree_node_holds (node, IDE_TYPE_PROJECT_FILE))
+ {
+ IdeProjectFile *project_file = ide_tree_node_get_item (node);
+ g_autoptr(GFile) file = ide_project_file_ref_file (project_file);
+
+ if (g_file_equal (find->file, file))
+ {
+ find->node = node;
+ return IDE_TREE_NODE_VISIT_BREAK;
+ }
+
+ if (g_file_has_prefix (find->file, file))
+ return IDE_TREE_NODE_VISIT_CHILDREN;
+ }
+
+ return IDE_TREE_NODE_VISIT_CONTINUE;
+}
+
+static IdeTreeNode *
+find_file_node (IdeTree *tree,
+ GFile *file)
+{
+ GtkTreeModel *model;
+ IdeTreeNode *root;
+ FindFileNode find;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_TREE (tree));
+ g_assert (G_IS_FILE (file));
+
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree));
+ root = ide_tree_model_get_root (IDE_TREE_MODEL (model));
+
+ find.file = file;
+ find.node = NULL;
+
+ ide_tree_node_traverse (root,
+ G_PRE_ORDER,
+ G_TRAVERSE_ALL,
+ -1,
+ traverse_cb,
+ &find);
+
+ return find.node;
+}
+
+static GList *
+collect_files (GFile *file,
+ GFile *stop_at)
+{
+ g_autoptr(GFile) copy = g_object_ref (file);
+ GList *list = NULL;
+
+ g_assert (g_file_has_prefix (file, stop_at));
+
+ while (!g_file_equal (copy, stop_at))
+ {
+ GFile *stolen = g_steal_pointer (©);
+
+ list = g_list_prepend (list, stolen);
+ copy = g_file_get_parent (stolen);
+ }
+
+ return g_steal_pointer (&list);
+}
+
+static void
+gbp_project_tree_addin_add_file (GbpProjectTreeAddin *self,
+ GFile *file)
+{
+ g_autolist(GFile) list = NULL;
+ g_autoptr(GFile) workdir = NULL;
+ IdeTreeNode *parent = NULL;
+ IdeContext *context;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (GBP_IS_PROJECT_TREE_ADDIN (self));
+ g_assert (G_IS_FILE (file));
+
+#ifdef IDE_ENABLE_TRACE
+ {
+ g_autofree gchar *uri = g_file_get_uri (file);
+ IDE_TRACE_MSG ("Adding file to tree \"%s\"", uri);
+ }
+#endif
+
+ context = ide_widget_get_context (GTK_WIDGET (self->tree));
+ workdir = ide_context_ref_workdir (context);
+
+ if (!g_file_has_prefix (file, workdir))
+ return;
+
+ list = collect_files (file, workdir);
+
+ for (const GList *iter = list; iter; iter = iter->next)
+ {
+ GFile *item = iter->data;
+ g_autoptr(IdeProjectFile) project_file = NULL;
+ g_autoptr(GFileInfo) info = NULL;
+ g_autoptr(IdeTreeNode) node = NULL;
+ g_autoptr(GFile) directory = NULL;
+
+ g_assert (G_IS_FILE (item));
+
+ if ((parent = find_file_node (self->tree, item)))
+ {
+ if (!ide_tree_node_expanded (self->tree, parent))
+ IDE_EXIT;
+
+ continue;
+ }
+
+ directory = g_file_get_parent (item);
+ parent = find_file_node (self->tree, directory);
+
+ info = g_file_query_info (item,
+ IDE_PROJECT_FILE_ATTRIBUTES,
+ G_FILE_QUERY_INFO_NONE,
+ NULL, NULL);
+
+ if (info == NULL)
+ IDE_EXIT;
+
+ project_file = ide_project_file_new (directory, info);
+ node = create_file_node (project_file);
+
+ /* TODO: Sort item */
+ ide_tree_node_append (parent, node);
+ }
+
+ IDE_EXIT;
+}
+
+static void
+gbp_project_tree_addin_remove_file (GbpProjectTreeAddin *self,
+ GFile *file)
+{
+ IdeTreeNode *selected;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (GBP_IS_PROJECT_TREE_ADDIN (self));
+ g_assert (G_IS_FILE (file));
+
+#ifdef IDE_ENABLE_TRACE
+ {
+ g_autofree gchar *uri = g_file_get_uri (file);
+ IDE_TRACE_MSG ("Removing file from tree \"%s\"", uri);
+ }
+#endif
+
+ if ((selected = find_file_node (self->tree, file)))
+ ide_tree_node_remove (ide_tree_node_get_parent (selected), selected);
+
+ IDE_EXIT;
+}
+
+static void
+gbp_project_tree_addin_changed_cb (GbpProjectTreeAddin *self,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event,
+ IdeVcsMonitor *monitor)
+{
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (GBP_IS_PROJECT_TREE_ADDIN (self));
+ g_assert (G_IS_FILE (file));
+ g_assert (!other_file || G_IS_FILE (other_file));
+ g_assert (IDE_IS_VCS_MONITOR (monitor));
+
+ if (event == G_FILE_MONITOR_EVENT_CREATED)
+ gbp_project_tree_addin_add_file (self, file);
+ else if (event == G_FILE_MONITOR_EVENT_DELETED)
+ gbp_project_tree_addin_remove_file (self, file);
+}
+
+static void
+gbp_project_tree_addin_reloaded_cb (GbpProjectTreeAddin *self,
+ IdeVcsMonitor *monitor)
+{
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (GBP_IS_PROJECT_TREE_ADDIN (self));
+ g_assert (IDE_IS_VCS_MONITOR (monitor));
+
+ gtk_widget_queue_resize (GTK_WIDGET (self->tree));
+}
+
+static void
+gbp_project_tree_addin_load (IdeTreeAddin *addin,
+ IdeTree *tree,
+ IdeTreeModel *model)
+{
+ static const GtkTargetEntry drag_targets[] = {
+ { (gchar *)"GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0 },
+ { (gchar *)"text/uri-list", 0, 0 },
+ };
+
+ GbpProjectTreeAddin *self = (GbpProjectTreeAddin *)addin;
+ IdeVcsMonitor *monitor;
+ IdeWorkbench *workbench;
+
+ g_assert (GBP_IS_PROJECT_TREE_ADDIN (self));
+ g_assert (IDE_IS_TREE_MODEL (model));
+
+ self->tree = tree;
+ self->model = model;
+
+ workbench = ide_widget_get_workbench (GTK_WIDGET (tree));
+ monitor = ide_workbench_get_vcs_monitor (workbench);
+
+ g_signal_connect_object (monitor,
+ "changed",
+ G_CALLBACK (gbp_project_tree_addin_changed_cb),
+ self,
+ G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (monitor,
+ "reloaded",
+ G_CALLBACK (gbp_project_tree_addin_reloaded_cb),
+ self,
+ G_CONNECT_SWAPPED);
+
+ gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (tree),
+ GDK_BUTTON1_MASK,
+ drag_targets, G_N_ELEMENTS (drag_targets),
+ GDK_ACTION_COPY | GDK_ACTION_MOVE);
+ gtk_tree_view_enable_model_drag_dest (GTK_TREE_VIEW (tree),
+ drag_targets, G_N_ELEMENTS (drag_targets),
+ GDK_ACTION_COPY | GDK_ACTION_MOVE);
+}
+
+static void
+gbp_project_tree_addin_unload (IdeTreeAddin *addin,
+ IdeTree *tree,
+ IdeTreeModel *model)
+{
+ GbpProjectTreeAddin *self = (GbpProjectTreeAddin *)addin;
+
+ g_assert (GBP_IS_PROJECT_TREE_ADDIN (self));
+ g_assert (IDE_IS_TREE_MODEL (model));
+
+ self->tree = NULL;
+ self->model = NULL;
+}
+
+static gboolean
+gbp_project_tree_addin_node_draggable (IdeTreeAddin *addin,
+ IdeTreeNode *node)
+{
+ return ide_tree_node_holds (node, IDE_TYPE_PROJECT_FILE);
+}
+
+static gboolean
+gbp_project_tree_addin_node_droppable (IdeTreeAddin *addin,
+ IdeTreeNode *drag_node,
+ IdeTreeNode *drop_node,
+ GtkSelectionData *selection)
+{
+ IdeProjectFile *drop_file = NULL;
+ g_auto(GStrv) uris = NULL;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (GBP_IS_PROJECT_TREE_ADDIN (addin));
+ g_assert (!drag_node || IDE_IS_TREE_NODE (drag_node));
+ g_assert (!drop_node || IDE_IS_TREE_NODE (drop_node));
+
+ /* Must drop on a file */
+ if (drop_node == NULL ||
+ !ide_tree_node_holds (drop_node, IDE_TYPE_PROJECT_FILE))
+ return FALSE;
+
+ /* The drop file must be a directory */
+ drop_file = ide_tree_node_get_item (drop_node);
+ if (!ide_project_file_is_directory (drop_file))
+ return FALSE;
+
+ /* We need a uri list or file node */
+ uris = gtk_selection_data_get_uris (selection);
+ if ((uris == NULL || uris[0] == NULL) && drag_node == NULL)
+ return FALSE;
+
+ /* If we have a drag node, make sure it's a file */
+ if (drag_node != NULL &&
+ !ide_tree_node_holds (drop_node, IDE_TYPE_PROJECT_FILE))
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+gbp_project_tree_addin_notify_progress_cb (DzlFileTransfer *transfer,
+ GParamSpec *pspec,
+ IdeNotification *notif)
+{
+ g_autofree gchar *body = NULL;
+ DzlFileTransferStat stbuf;
+ gchar count[16];
+ gchar total[16];
+ gdouble progress;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (DZL_IS_FILE_TRANSFER (transfer));
+ g_assert (IDE_IS_NOTIFICATION (notif));
+
+ dzl_file_transfer_stat (transfer, &stbuf);
+
+ progress = dzl_file_transfer_get_progress (transfer);
+ ide_notification_set_progress (notif, progress);
+
+ g_snprintf (count, sizeof count, "%"G_GINT64_FORMAT, stbuf.n_files);
+ g_snprintf (total, sizeof total, "%"G_GINT64_FORMAT, stbuf.n_files_total);
+
+ if (stbuf.n_files_total == 1)
+ body = g_strdup_printf (_("Copying 1 file"));
+ else
+ /* translators: first %s is replaced with completed number of files, second %s with total number of
files */
+ body = g_strdup_printf (_("Copying %s of %s files"), count, total);
+
+ ide_notification_set_body (notif, body);
+}
+
+static void
+gbp_project_tree_addin_transfer_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ DzlFileTransfer *transfer = (DzlFileTransfer *)object;
+ g_autoptr(IdeTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+ GbpProjectTreeAddin *self;
+ IdeNotification *notif;
+ DzlFileTransferStat stbuf;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (DZL_IS_FILE_TRANSFER (transfer));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_TASK (task));
+
+ self = ide_task_get_source_object (task);
+ notif = ide_task_get_task_data (task);
+
+ g_assert (GBP_IS_PROJECT_TREE_ADDIN (self));
+ g_assert (notif != NULL);
+ g_assert (IDE_IS_NOTIFICATION (notif));
+
+ gbp_project_tree_addin_notify_progress_cb (transfer, NULL, notif);
+ ide_notification_set_progress (notif, 1.0);
+
+ if (!dzl_file_transfer_execute_finish (transfer, result, &error))
+ {
+ ide_notification_set_title (notif, _("Failed to copy files"));
+ ide_notification_set_body (notif, error->message);
+ ide_task_return_error (task, g_steal_pointer (&error));
+ }
+ else
+ {
+ GPtrArray *sources;
+
+ dzl_file_transfer_stat (transfer, &stbuf);
+
+ ide_notification_set_title (notif, _("Files copied"));
+
+ if (stbuf.n_files_total == 1)
+ {
+ ide_notification_set_body (notif, _("Copied 1 file"));
+ }
+ else
+ {
+ g_autofree gchar *format = NULL;
+ gchar count[16];
+
+ g_snprintf (count, sizeof count, "%"G_GINT64_FORMAT, stbuf.n_files_total);
+ format = g_strdup_printf (_("Copied %s files"), count);
+ ide_notification_set_body (notif, format);
+ }
+
+ sources = g_object_get_data (G_OBJECT (task), "SOURCE_FILES");
+
+ if (sources != NULL)
+ {
+ IdeContext *context;
+ IdeProject *project;
+
+ /*
+ * We avoid deleting files here and instead just trash the
+ * existing files to help reduce any chance that we delete
+ * user data.
+ *
+ * Also, this will only trash files that are within our
+ * project directory. Currently, I'm considering that a
+ * feature, but when I trust file-deletion more, we can
+ * open it up in IdeProject.
+ */
+
+ context = ide_object_get_context (IDE_OBJECT (self->model));
+ project = ide_project_from_context (context);
+
+ for (guint i = 0; i < sources->len; i++)
+ {
+ GFile *source = g_ptr_array_index (sources, i);
+
+ g_assert (G_IS_FILE (source));
+
+ ide_project_trash_file_async (project, source, NULL, NULL, NULL);
+ }
+ }
+
+ ide_task_return_boolean (task, TRUE);
+ }
+
+ ide_notification_withdraw_in_seconds (notif, -1);
+
+ IDE_EXIT;
+}
+
+static void
+gbp_project_tree_addin_node_dropped_async (IdeTreeAddin *addin,
+ IdeTreeNode *drag_node,
+ IdeTreeNode *drop_node,
+ GtkSelectionData *selection,
+ GdkDragAction actions,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GbpProjectTreeAddin *self = (GbpProjectTreeAddin *)addin;
+ g_autoptr(IdeTask) task = NULL;
+ g_autoptr(DzlFileTransfer) transfer = NULL;
+ g_autoptr(GFile) src_file = NULL;
+ g_autoptr(GFile) dst_dir = NULL;
+ g_autoptr(IdeNotification) notif = NULL;
+ g_autoptr(GPtrArray) srcs = NULL;
+ g_auto(GStrv) uris = NULL;
+ IdeProjectFile *drag_file;
+ IdeProjectFile *drop_file;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (GBP_IS_PROJECT_TREE_ADDIN (self));
+ g_assert (!drag_node || IDE_IS_TREE_NODE (drag_node));
+ g_assert (!drop_node || IDE_IS_TREE_NODE (drop_node));
+
+ task = ide_task_new (self, cancellable, callback, user_data);
+ ide_task_set_source_tag (task, gbp_project_tree_addin_node_dropped_async);
+
+ if (!gbp_project_tree_addin_node_droppable (addin, drag_node, drop_node, selection))
+ {
+ ide_task_return_boolean (task, TRUE);
+ IDE_EXIT;
+ }
+
+ srcs = g_ptr_array_new_with_free_func (g_object_unref);
+ uris = gtk_selection_data_get_uris (selection);
+
+ if (uris != NULL)
+ {
+ for (guint i = 0; uris[i]; i++)
+ g_ptr_array_add (srcs, g_file_new_for_uri (uris[i]));
+ }
+
+ drop_file = ide_tree_node_get_item (drop_node);
+ g_assert (drop_file != NULL);
+ g_assert (ide_project_file_is_directory (drop_file));
+
+ if (drag_node != NULL)
+ {
+ drag_file = ide_tree_node_get_item (drag_node);
+ src_file = ide_project_file_ref_file (drag_file);
+ g_assert (G_IS_FILE (src_file));
+ g_ptr_array_add (srcs, g_object_ref (src_file));
+ }
+
+ dst_dir = ide_project_file_ref_file (drop_file);
+ g_assert (G_IS_FILE (dst_dir));
+
+ transfer = dzl_file_transfer_new ();
+ dzl_file_transfer_set_flags (transfer, DZL_FILE_TRANSFER_FLAGS_NONE);
+ g_signal_connect_object (transfer,
+ "notify::progress",
+ G_CALLBACK (gbp_project_tree_addin_notify_progress_cb),
+ notif,
+ 0);
+
+ for (guint i = 0; i < srcs->len; i++)
+ {
+ GFile *source = g_ptr_array_index (srcs, i);
+ g_autofree gchar *name = NULL;
+ g_autoptr(GFile) dst_file = NULL;
+
+ name = g_file_get_basename (source);
+ g_assert (name != NULL);
+
+ dst_file = g_file_get_child (dst_dir, name);
+ g_assert (G_IS_FILE (dst_file));
+
+ if (srcs->len == 1 && g_file_equal (source, dst_file))
+ {
+ ide_task_return_boolean (task, TRUE);
+ IDE_EXIT;
+ }
+
+ dzl_file_transfer_add (transfer, source, dst_file);
+ }
+
+ if (actions == GDK_ACTION_MOVE)
+ g_object_set_data_full (G_OBJECT (task),
+ "SOURCE_FILES",
+ g_steal_pointer (&srcs),
+ (GDestroyNotify)g_ptr_array_unref);
+
+ notif = ide_notification_new ();
+ ide_notification_set_title (notif, _("Copying files…"));
+ ide_notification_set_body (notif, _("Files will be copied in a moment"));
+ ide_notification_set_has_progress (notif, TRUE);
+ ide_notification_attach (notif, IDE_OBJECT (self->model));
+ ide_task_set_task_data (task, g_object_ref (notif), g_object_unref);
+
+ dzl_file_transfer_execute_async (transfer,
+ G_PRIORITY_DEFAULT,
+ cancellable,
+ gbp_project_tree_addin_transfer_cb,
+ g_steal_pointer (&task));
+
+ IDE_EXIT;
+}
+
+static gboolean
+gbp_project_tree_addin_node_dropped_finish (IdeTreeAddin *addin,
+ GAsyncResult *result,
+ GError **error)
+{
+ gboolean ret;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (GBP_IS_PROJECT_TREE_ADDIN (addin));
+ g_assert (IDE_IS_TASK (result));
+
+ ret = ide_task_propagate_boolean (IDE_TASK (result), error);
+
+ IDE_RETURN (ret);
+}
+
+static void
+tree_addin_iface_init (IdeTreeAddinInterface *iface)
+{
+ iface->load = gbp_project_tree_addin_load;
+ iface->unload = gbp_project_tree_addin_unload;
+ iface->build_children_async = gbp_project_tree_addin_build_children_async;
+ iface->build_children_finish = gbp_project_tree_addin_build_children_finish;
+ iface->node_activated = gbp_project_tree_addin_node_activated;
+ iface->node_draggable = gbp_project_tree_addin_node_draggable;
+ iface->node_droppable = gbp_project_tree_addin_node_droppable;
+ iface->node_dropped_async = gbp_project_tree_addin_node_dropped_async;
+ iface->node_dropped_finish = gbp_project_tree_addin_node_dropped_finish;
+}
+
+static void
+gbp_project_tree_addin_settings_changed (GbpProjectTreeAddin *self,
+ const gchar *key,
+ GSettings *settings)
+{
+ g_assert (GBP_IS_PROJECT_TREE_ADDIN (self));
+ g_assert (G_IS_SETTINGS (settings));
+
+ self->sort_directories_first = g_settings_get_boolean (self->settings, "sort-directories-first");
+ self->show_ignored_files = g_settings_get_boolean (self->settings, "show-ignored-files");
+
+ if (self->model != NULL)
+ ide_tree_model_invalidate (self->model, NULL);
+}
+
+G_DEFINE_TYPE_WITH_CODE (GbpProjectTreeAddin, gbp_project_tree_addin, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (IDE_TYPE_TREE_ADDIN, tree_addin_iface_init))
+
+static void
+gbp_project_tree_addin_dispose (GObject *object)
+{
+ GbpProjectTreeAddin *self = (GbpProjectTreeAddin *)object;
+
+ g_clear_object (&self->settings);
+
+ G_OBJECT_CLASS (gbp_project_tree_addin_parent_class)->dispose (object);
+}
+
+static void
+gbp_project_tree_addin_class_init (GbpProjectTreeAddinClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = gbp_project_tree_addin_dispose;
+}
+
+static void
+gbp_project_tree_addin_init (GbpProjectTreeAddin *self)
+{
+ self->settings = g_settings_new ("org.gnome.builder.project-tree");
+
+ g_signal_connect_object (self->settings,
+ "changed",
+ G_CALLBACK (gbp_project_tree_addin_settings_changed),
+ self,
+ G_CONNECT_SWAPPED);
+
+ gbp_project_tree_addin_settings_changed (self, NULL, self->settings);
+}
diff --git a/src/plugins/project-tree/gb-project-tree-addin.h
b/src/plugins/project-tree/gbp-project-tree-addin.h
similarity index 74%
rename from src/plugins/project-tree/gb-project-tree-addin.h
rename to src/plugins/project-tree/gbp-project-tree-addin.h
index 0ff3faf8d..525b8fba3 100644
--- a/src/plugins/project-tree/gb-project-tree-addin.h
+++ b/src/plugins/project-tree/gbp-project-tree-addin.h
@@ -1,6 +1,6 @@
-/* gb-project-tree-addin.h
+/* gbp-project-tree-addin.h
*
- * Copyright 2015-2019 Christian Hergert <chergert redhat com>
+ * Copyright 2018-2019 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
@@ -24,8 +24,8 @@
G_BEGIN_DECLS
-#define GB_TYPE_PROJECT_TREE_ADDIN (gb_project_tree_addin_get_type())
+#define GBP_TYPE_PROJECT_TREE_ADDIN (gbp_project_tree_addin_get_type())
-G_DECLARE_FINAL_TYPE (GbProjectTreeAddin, gb_project_tree_addin, GB, PROJECT_TREE_ADDIN, GObject)
+G_DECLARE_FINAL_TYPE (GbpProjectTreeAddin, gbp_project_tree_addin, GBP, PROJECT_TREE_ADDIN, GObject)
G_END_DECLS
diff --git a/src/plugins/project-tree/gbp-project-tree-pane-actions.c
b/src/plugins/project-tree/gbp-project-tree-pane-actions.c
new file mode 100644
index 000000000..acb795a0a
--- /dev/null
+++ b/src/plugins/project-tree/gbp-project-tree-pane-actions.c
@@ -0,0 +1,634 @@
+/* gbp-project-tree-pane-actions.c
+ *
+ * Copyright 2018-2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "gbp-project-tree-pane-actions"
+
+#include "config.h"
+
+#include <libide-editor.h>
+#include <libide-projects.h>
+#include <vte/vte.h>
+
+#include "gbp-project-tree-private.h"
+#include "gbp-rename-file-popover.h"
+#include "gbp-new-file-popover.h"
+
+typedef struct
+{
+ IdeTreeNode *node;
+ GFile *file;
+ GFileType file_type;
+ guint needs_collapse : 1;
+} NewState;
+
+static void
+new_state_free (NewState *state)
+{
+ g_clear_object (&state->node);
+ g_clear_object (&state->file);
+ g_slice_free (NewState, state);
+}
+
+static void
+new_action_completed_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GbpProjectTreePane *self = (GbpProjectTreePane *)object;
+ NewState *state;
+
+ g_assert (GBP_IS_PROJECT_TREE_PANE (self));
+ g_assert (IDE_IS_TASK (result));
+
+ state = ide_task_get_task_data (IDE_TASK (result));
+ g_assert (state != NULL);
+ g_assert (IDE_IS_TREE_NODE (state->node));
+
+ if (state->needs_collapse)
+ ide_tree_collapse_node (self->tree, state->node);
+
+ /* Open the file if we created a regular file */
+ if (state->file_type == G_FILE_TYPE_REGULAR)
+ {
+ IdeWorkbench *workbench;
+
+ if (!(workbench = ide_widget_get_workbench (GTK_WIDGET (self->tree))))
+ return;
+
+ if (state->file != NULL)
+ ide_workbench_open_async (workbench, state->file, "editor", 0, NULL, NULL, NULL);
+ }
+}
+
+static void
+gbp_project_tree_pane_actions_mkdir_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GFile *file = (GFile *)object;
+ g_autoptr(IdeTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+
+ g_assert (G_IS_FILE (file));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_TASK (task));
+
+ if (!g_file_make_directory_finish (file, result, &error))
+ g_warning ("Failed to make directory: %s", error->message);
+
+ ide_task_return_boolean (task, TRUE);
+}
+
+static void
+gbp_project_tree_pane_actions_mkfile_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GFile *file = (GFile *)object;
+ g_autoptr(IdeTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+
+ g_assert (G_IS_FILE (file));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_TASK (task));
+
+ if (!g_file_create_finish (file, result, &error))
+ g_warning ("Failed to make file: %s", error->message);
+
+ ide_task_return_boolean (task, TRUE);
+}
+
+static void
+gbp_project_tree_pane_actions_new_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GbpNewFilePopover *popover = (GbpNewFilePopover *)object;
+ g_autoptr(IdeTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GFile) file = NULL;
+ GCancellable *cancellable;
+ NewState *state;
+
+ g_assert (GBP_IS_NEW_FILE_POPOVER (popover));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_TASK (task));
+
+ if (!(file = gbp_new_file_popover_display_finish (popover, result, &error)))
+ {
+ ide_task_return_error (task, g_steal_pointer (&error));
+ return;
+ }
+
+ cancellable = ide_task_get_cancellable (task);
+ state = ide_task_get_task_data (task);
+
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+ g_assert (state != NULL);
+ g_assert (IDE_IS_TREE_NODE (state->node));
+ g_assert (state->file_type);
+ g_assert (state->file == NULL);
+
+ state->file = g_object_ref (file);
+
+ if (state->file_type == G_FILE_TYPE_DIRECTORY)
+ g_file_make_directory_async (file,
+ G_PRIORITY_DEFAULT,
+ cancellable,
+ gbp_project_tree_pane_actions_mkdir_cb,
+ g_steal_pointer (&task));
+ else if (state->file_type == G_FILE_TYPE_REGULAR)
+ g_file_create_async (file,
+ G_FILE_CREATE_NONE,
+ G_PRIORITY_DEFAULT,
+ cancellable,
+ gbp_project_tree_pane_actions_mkfile_cb,
+ g_steal_pointer (&task));
+ else
+ g_assert_not_reached ();
+
+ gtk_widget_destroy (GTK_WIDGET (popover));
+}
+
+static void
+gbp_project_tree_pane_actions_new (GbpProjectTreePane *self,
+ GFileType file_type)
+{
+ g_autoptr(IdeTask) task = NULL;
+ g_autoptr(GFile) directory = NULL;
+ GbpNewFilePopover *popover;
+ IdeProjectFile *project_file;
+ IdeTreeNode *selected;
+ NewState *state;
+
+ g_assert (GBP_IS_PROJECT_TREE_PANE (self));
+ g_assert (file_type == G_FILE_TYPE_REGULAR ||
+ file_type == G_FILE_TYPE_DIRECTORY);
+
+ /* Nothing to do if there was no selection */
+ if (!(selected = ide_tree_get_selected_node (self->tree)))
+ return;
+
+ /* Select parent if we got an empty node or it's not a directory */
+ if (!ide_tree_node_holds (selected, IDE_TYPE_PROJECT_FILE) ||
+ !(project_file = ide_tree_node_get_item (selected)) ||
+ !ide_project_file_is_directory (project_file))
+ {
+ IdeTreeNode *parent = ide_tree_node_get_parent (selected);
+
+ if (!ide_tree_node_holds (parent, IDE_TYPE_PROJECT_FILE))
+ return;
+
+ project_file = ide_tree_node_get_item (parent);
+ selected = parent;
+
+ ide_tree_select_node (self->tree, parent);
+ }
+
+ /* Now create our async task to keep track of everything during
+ * the asynchronous nature of this workflow (the user entering
+ * infromation, maybe cancelling, and async file creation).
+ */
+ directory = ide_project_file_ref_file (project_file);
+
+ popover = g_object_new (GBP_TYPE_NEW_FILE_POPOVER,
+ "directory", directory,
+ "file-type", file_type,
+ "position", GTK_POS_RIGHT,
+ NULL);
+
+
+ state = g_slice_new0 (NewState);
+ state->needs_collapse = !ide_tree_node_expanded (self->tree, selected);
+ state->file_type = file_type;
+ state->node = g_object_ref (selected);
+
+ task = ide_task_new (self, NULL, new_action_completed_cb, NULL);
+ ide_task_set_source_tag (task, gbp_project_tree_pane_actions_new);
+ ide_task_set_task_data (task, state, new_state_free);
+
+ gbp_new_file_popover_display_async (popover,
+ self->tree,
+ selected,
+ NULL,
+ gbp_project_tree_pane_actions_new_cb,
+ g_steal_pointer (&task));
+}
+
+static void
+close_matching_pages (GtkWidget *widget,
+ gpointer user_data)
+{
+ IdePage *page = (IdePage *)widget;
+ GFile *file = user_data;
+ GFile *this_file;
+
+ g_assert (IDE_IS_PAGE (page));
+ g_assert (G_IS_FILE (file));
+
+ if (!IDE_IS_EDITOR_PAGE (page))
+ return;
+
+ if (!(this_file = ide_editor_page_get_file (IDE_EDITOR_PAGE (page))))
+ return;
+
+ if (g_file_equal (this_file, file))
+ gtk_widget_destroy (widget);
+}
+
+#define DEFINE_ACTION_HANDLER(short_name, BODY) \
+static void \
+gbp_project_tree_pane_actions_##short_name (GSimpleAction *action, \
+ GVariant *param, \
+ gpointer user_data) \
+{ \
+ GbpProjectTreePane *self = user_data; \
+ \
+ g_assert (G_IS_SIMPLE_ACTION (action)); \
+ g_assert (GBP_IS_PROJECT_TREE_PANE (self)); \
+ \
+ BODY \
+}
+
+DEFINE_ACTION_HANDLER (new_file, {
+ gbp_project_tree_pane_actions_new (self, G_FILE_TYPE_REGULAR);
+});
+
+DEFINE_ACTION_HANDLER (new_folder, {
+ gbp_project_tree_pane_actions_new (self, G_FILE_TYPE_DIRECTORY);
+});
+
+DEFINE_ACTION_HANDLER (open, {
+ IdeProjectFile *project_file;
+ g_autoptr(GFile) file = NULL;
+ IdeWorkbench *workbench;
+ IdeTreeNode *selected;
+
+ if (!(selected = ide_tree_get_selected_node (self->tree)) ||
+ !ide_tree_node_holds (selected, IDE_TYPE_PROJECT_FILE) ||
+ !(project_file = ide_tree_node_get_item (selected)))
+ return;
+
+ file = ide_project_file_ref_file (project_file);
+ workbench = ide_widget_get_workbench (GTK_WIDGET (self));
+
+ ide_workbench_open_async (workbench,
+ file,
+ NULL,
+ IDE_BUFFER_OPEN_FLAGS_NONE,
+ NULL, NULL, NULL);
+});
+
+static void
+gbp_project_tree_pane_actions_rename_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeProject *project = (IdeProject *)object;
+ g_autoptr(GbpProjectTreePane) self = user_data;
+ g_autoptr(GError) error = NULL;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (GBP_IS_PROJECT_TREE_PANE (self));
+
+ if (!ide_project_rename_file_finish (project, result, &error))
+ g_warning ("Failed to rename file: %s", error->message);
+}
+
+static void
+gbp_project_tree_pane_actions_rename_display_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GbpRenameFilePopover *popover = (GbpRenameFilePopover *)object;
+ g_autoptr(GbpProjectTreePane) self = user_data;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GFile) dst = NULL;
+ IdeProject *project;
+ IdeContext *context;
+ GFile *src;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (GBP_IS_PROJECT_TREE_PANE (self));
+
+ if (!(dst = gbp_rename_file_popover_display_finish (popover, result, &error)))
+ goto destroy;
+
+ src = gbp_rename_file_popover_get_file (popover);
+ context = ide_widget_get_context (GTK_WIDGET (self));
+ project = ide_project_from_context (context);
+
+ ide_project_rename_file_async (project,
+ src,
+ dst,
+ NULL,
+ gbp_project_tree_pane_actions_rename_cb,
+ g_object_ref (self));
+
+destroy:
+ gtk_widget_destroy (GTK_WIDGET (popover));
+}
+
+DEFINE_ACTION_HANDLER (rename, {
+ IdeProjectFile *project_file;
+ g_autoptr(GFile) file = NULL;
+ GbpRenameFilePopover *popover;
+ IdeWorkbench *workbench;
+ IdeTreeNode *selected;
+ gboolean is_dir;
+
+ if (!(selected = ide_tree_get_selected_node (self->tree)) ||
+ !ide_tree_node_holds (selected, IDE_TYPE_PROJECT_FILE) ||
+ !(project_file = ide_tree_node_get_item (selected)))
+ return;
+
+ is_dir = ide_project_file_is_directory (project_file);
+ file = ide_project_file_ref_file (project_file);
+ workbench = ide_widget_get_workbench (GTK_WIDGET (self->tree));
+ ide_workbench_foreach_page (workbench, close_matching_pages, file);
+
+ popover = g_object_new (GBP_TYPE_RENAME_FILE_POPOVER,
+ "position", GTK_POS_LEFT,
+ "is-directory", is_dir,
+ "file", file,
+ NULL);
+
+ gbp_rename_file_popover_display_async (popover,
+ self->tree,
+ selected,
+ NULL,
+ gbp_project_tree_pane_actions_rename_display_cb,
+ g_object_ref (self));
+});
+
+static void
+gbp_project_tree_pane_actions_trash_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeProjectFile *project_file = (IdeProjectFile *)object;
+ g_autoptr(IdeTreeNode) node = user_data;
+ g_autoptr(GError) error = NULL;
+ IdeTreeNode *parent;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_PROJECT_FILE (project_file));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_TREE_NODE (node));
+
+ if (!ide_project_file_trash_finish (project_file, result, &error))
+ return;
+
+ if ((parent = ide_tree_node_get_parent (node)))
+ ide_tree_node_remove (parent, node);
+}
+
+DEFINE_ACTION_HANDLER (trash, {
+ IdeProjectFile *project_file;
+ g_autoptr(GFile) file = NULL;
+ IdeWorkbench *workbench;
+ IdeTreeNode *selected;
+
+ if (!(selected = ide_tree_get_selected_node (self->tree)) ||
+ !ide_tree_node_holds (selected, IDE_TYPE_PROJECT_FILE) ||
+ !(project_file = ide_tree_node_get_item (selected)))
+ return;
+
+ file = ide_project_file_ref_file (project_file);
+ workbench = ide_widget_get_workbench (GTK_WIDGET (self->tree));
+ ide_workbench_foreach_page (workbench, close_matching_pages, file);
+
+ ide_project_file_trash_async (project_file,
+ NULL,
+ gbp_project_tree_pane_actions_trash_cb,
+ g_object_ref (selected));
+});
+
+DEFINE_ACTION_HANDLER (open_containing_folder, {
+ IdeProjectFile *project_file;
+ g_autoptr(GFile) file = NULL;
+ IdeTreeNode *selected;
+
+ if (!(selected = ide_tree_get_selected_node (self->tree)) ||
+ !ide_tree_node_holds (selected, IDE_TYPE_PROJECT_FILE) ||
+ !(project_file = ide_tree_node_get_item (selected)))
+ return;
+
+ file = ide_project_file_ref_file (project_file);
+ dzl_file_manager_show (file, NULL);
+});
+
+DEFINE_ACTION_HANDLER (open_with_hint, {
+ IdeProjectFile *project_file;
+ g_autoptr(GFile) file = NULL;
+ IdeWorkbench *workbench;
+ IdeTreeNode *selected;
+ const gchar *hint;
+
+ if (!(selected = ide_tree_get_selected_node (self->tree)) ||
+ !ide_tree_node_holds (selected, IDE_TYPE_PROJECT_FILE) ||
+ !(project_file = ide_tree_node_get_item (selected)) ||
+ !(hint = g_variant_get_string (param, NULL)))
+ return;
+
+ workbench = ide_widget_get_workbench (GTK_WIDGET (self));
+ file = ide_project_file_ref_file (project_file);
+
+ ide_workbench_open_async (workbench,
+ file,
+ hint,
+ IDE_BUFFER_OPEN_FLAGS_NONE,
+ NULL, NULL, NULL);
+});
+
+/* Based on gdesktopappinfo.c in GIO */
+static gchar *
+find_terminal_executable (void)
+{
+ gsize i;
+ gchar *path = NULL;
+ g_autoptr(GSettings) terminal_settings = NULL;
+ g_autofree gchar *gsettings_terminal = NULL;
+ const gchar *terminals[] = {
+ NULL, /* GSettings */
+ "x-terminal-emulator", /* Debian's alternative system */
+ "gnome-terminal",
+ NULL, /* getenv ("TERM") */
+ "nxterm", "color-xterm",
+ "rxvt", "xterm", "dtterm"
+ };
+
+ /* This is deprecated, but at least the user can specify it! */
+ terminal_settings = g_settings_new ("org.gnome.desktop.default-applications.terminal");
+ gsettings_terminal = g_settings_get_string (terminal_settings, "exec");
+ terminals[0] = gsettings_terminal;
+
+ /* This is generally one of the fallback terminals */
+ terminals[3] = g_getenv ("TERM");
+
+ for (i = 0; i < G_N_ELEMENTS (terminals) && path == NULL; ++i)
+ {
+ if (terminals[i] != NULL)
+ {
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+ path = g_find_program_in_path (terminals[i]);
+ G_GNUC_END_IGNORE_DEPRECATIONS
+ }
+ }
+
+ return path;
+}
+
+static void
+gbp_project_tree_pane_actions_open_in_terminal (GSimpleAction *action,
+ GVariant *param,
+ gpointer user_data)
+{
+ GbpProjectTreePane *self = user_data;
+ IdeProjectFile *project_file;
+ g_autoptr(GFile) file = NULL;
+ IdeTreeNode *selected;
+ g_autofree gchar *terminal_executable = NULL;
+ const gchar *argv[] = { NULL, NULL };
+ g_auto(GStrv) env = NULL;
+ g_autoptr(GFile) workdir = NULL;
+ g_autoptr(GError) error = NULL;
+
+ if (!(selected = ide_tree_get_selected_node (self->tree)) ||
+ !ide_tree_node_holds (selected, IDE_TYPE_PROJECT_FILE) ||
+ !(project_file = ide_tree_node_get_item (selected)))
+ return;
+
+ if (ide_project_file_is_directory (project_file))
+ workdir = ide_project_file_ref_file (project_file);
+ else
+ workdir = g_object_ref (ide_project_file_get_directory (project_file));
+
+ if (!g_file_is_native (workdir))
+ {
+ g_warning ("Not a native directory, cannot open terminal");
+ return;
+ }
+
+ terminal_executable = find_terminal_executable ();
+ argv[0] = terminal_executable;
+ g_return_if_fail (terminal_executable != NULL);
+
+ env = g_get_environ ();
+
+ {
+ /*
+ * Overwrite SHELL to the users default shell.
+ * Failure to do so typically results in /bin/sh being used.
+ */
+ g_autofree gchar *shell = vte_get_user_shell ();
+ env = g_environ_setenv (env, "SHELL", shell, TRUE);
+ }
+
+ /* Can't use GdkAppLaunchContext as
+ * we cannot set the working directory.
+ */
+ if (!g_spawn_async (g_file_peek_path (workdir),
+ (gchar **)argv, env,
+ G_SPAWN_STDERR_TO_DEV_NULL,
+ NULL, NULL, NULL, &error))
+ /* translators: %s is replaced with the error message */
+ g_warning ("Failed to spawn terminal: %s", error->message);
+}
+
+static const GActionEntry entries[] = {
+ { "new-file", gbp_project_tree_pane_actions_new_file },
+ { "new-folder", gbp_project_tree_pane_actions_new_folder },
+ { "open", gbp_project_tree_pane_actions_open },
+ { "open-with-hint", gbp_project_tree_pane_actions_open_with_hint, "s" },
+ { "open-containing-folder", gbp_project_tree_pane_actions_open_containing_folder },
+ { "open-in-terminal", gbp_project_tree_pane_actions_open_in_terminal },
+ { "rename", gbp_project_tree_pane_actions_rename },
+ { "trash", gbp_project_tree_pane_actions_trash },
+};
+
+void
+_gbp_project_tree_pane_init_actions (GbpProjectTreePane *self)
+{
+ g_autoptr(GSimpleActionGroup) actions = NULL;
+
+ g_assert (GBP_IS_PROJECT_TREE_PANE (self));
+
+ actions = g_simple_action_group_new ();
+ g_action_map_add_action_entries (G_ACTION_MAP (actions),
+ entries,
+ G_N_ELEMENTS (entries),
+ self);
+ gtk_widget_insert_action_group (GTK_WIDGET (self->tree),
+ "project-tree",
+ G_ACTION_GROUP (actions));
+
+ _gbp_project_tree_pane_update_actions (self);
+}
+
+void
+_gbp_project_tree_pane_update_actions (GbpProjectTreePane *self)
+{
+ GtkTreeSelection *selection;
+ gboolean is_file = FALSE;
+ gboolean is_dir = FALSE;
+
+ g_assert (GBP_IS_PROJECT_TREE_PANE (self));
+
+ if ((selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->tree))))
+ {
+ GtkTreeIter iter;
+
+ if (gtk_tree_selection_get_selected (selection, NULL, &iter))
+ {
+ IdeTreeModel *model = IDE_TREE_MODEL (gtk_tree_view_get_model (GTK_TREE_VIEW (self->tree)));
+ IdeTreeNode *node = ide_tree_model_get_node (model, &iter);
+ GObject *item = ide_tree_node_get_item (node);
+
+ if ((is_file = IDE_IS_PROJECT_FILE (item)))
+ is_dir = ide_project_file_is_directory (IDE_PROJECT_FILE (item));
+ }
+ }
+
+ dzl_gtk_widget_action_set (GTK_WIDGET (self->tree), "project-tree", "new-file",
+ "enabled", is_file,
+ NULL);
+ dzl_gtk_widget_action_set (GTK_WIDGET (self->tree), "project-tree", "new-folder",
+ "enabled", is_file,
+ NULL);
+ dzl_gtk_widget_action_set (GTK_WIDGET (self->tree), "project-tree", "trash",
+ "enabled", is_file,
+ NULL);
+ dzl_gtk_widget_action_set (GTK_WIDGET (self->tree), "project-tree", "rename",
+ "enabled", is_file,
+ NULL);
+ dzl_gtk_widget_action_set (GTK_WIDGET (self->tree), "project-tree", "open",
+ "enabled", is_file && !is_dir,
+ NULL);
+ dzl_gtk_widget_action_set (GTK_WIDGET (self->tree), "project-tree", "open-with-hint",
+ "enabled", is_file && !is_dir,
+ NULL);
+ dzl_gtk_widget_action_set (GTK_WIDGET (self->tree), "project-tree", "open-containing-folder",
+ "enabled", is_file,
+ NULL);
+ dzl_gtk_widget_action_set (GTK_WIDGET (self->tree), "project-tree", "open-in-terminal",
+ "enabled", is_file,
+ NULL);
+}
diff --git a/src/plugins/project-tree/gbp-project-tree-pane.c
b/src/plugins/project-tree/gbp-project-tree-pane.c
new file mode 100644
index 000000000..9070e4b92
--- /dev/null
+++ b/src/plugins/project-tree/gbp-project-tree-pane.c
@@ -0,0 +1,62 @@
+/* gbp-project-tree-pane.c
+ *
+ * Copyright 2018-2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "gbp-project-tree-pane"
+
+#include "config.h"
+
+#include "gbp-project-tree-private.h"
+#include "gbp-project-tree.h"
+
+G_DEFINE_TYPE (GbpProjectTreePane, gbp_project_tree_pane, IDE_TYPE_PANE)
+
+static void
+gbp_project_tree_pane_class_init (GbpProjectTreePaneClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ gtk_widget_class_set_template_from_resource (widget_class,
"/plugins/project-tree/gbp-project-tree-pane.ui");
+ gtk_widget_class_bind_template_child (widget_class, GbpProjectTreePane, tree);
+
+ g_type_ensure (GBP_TYPE_PROJECT_TREE);
+}
+
+static void
+gbp_project_tree_pane_init (GbpProjectTreePane *self)
+{
+ GtkTreeSelection *selection;
+ IdeApplication *app;
+ GMenu *menu;
+
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ app = IDE_APPLICATION_DEFAULT;
+ menu = dzl_application_get_menu_by_id (DZL_APPLICATION (app), "project-tree-menu");
+ ide_tree_set_context_menu (self->tree, menu);
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self->tree));
+ g_signal_connect_object (selection,
+ "changed",
+ G_CALLBACK (_gbp_project_tree_pane_update_actions),
+ self,
+ G_CONNECT_SWAPPED);
+
+ _gbp_project_tree_pane_init_actions (self);
+}
diff --git a/src/plugins/project-tree/gb-project-tree-editor-addin.h
b/src/plugins/project-tree/gbp-project-tree-pane.h
similarity index 70%
rename from src/plugins/project-tree/gb-project-tree-editor-addin.h
rename to src/plugins/project-tree/gbp-project-tree-pane.h
index 013bc6ce2..9a47d78eb 100644
--- a/src/plugins/project-tree/gb-project-tree-editor-addin.h
+++ b/src/plugins/project-tree/gbp-project-tree-pane.h
@@ -1,6 +1,6 @@
-/* gb-project-tree-editor-addin.h
+/* gbp-project-tree-pane.h
*
- * Copyright 2015-2019 Christian Hergert <chergert redhat com>
+ * Copyright 2018-2019 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
@@ -20,12 +20,12 @@
#pragma once
-#include <ide.h>
+#include <libide-gui.h>
G_BEGIN_DECLS
-#define GB_TYPE_PROJECT_TREE_EDITOR_ADDIN (gb_project_tree_editor_addin_get_type())
+#define GBP_TYPE_PROJECT_TREE_PANE (gbp_project_tree_pane_get_type())
-G_DECLARE_FINAL_TYPE (GbProjectTreeEditorAddin, gb_project_tree_editor_addin, GB, PROJECT_TREE_EDITOR_ADDIN,
GObject)
+G_DECLARE_FINAL_TYPE (GbpProjectTreePane, gbp_project_tree_pane, GBP, PROJECT_TREE_PANE, IdePane)
G_END_DECLS
diff --git a/src/plugins/project-tree/gbp-project-tree-pane.ui
b/src/plugins/project-tree/gbp-project-tree-pane.ui
new file mode 100644
index 000000000..c99ca7bbd
--- /dev/null
+++ b/src/plugins/project-tree/gbp-project-tree-pane.ui
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="GbpProjectTreePane" parent="IdePane">
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">true</property>
+ <child>
+ <object class="GbpProjectTree" id="tree">
+ <property name="level-indentation">16</property>
+ <property name="visible">true</property>
+ <style>
+ <class name="i-wanna-be-list-box"/>
+ <class name="project-tree"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/src/plugins/project-tree/gb-project-tree-private.h
b/src/plugins/project-tree/gbp-project-tree-private.h
similarity index 64%
rename from src/plugins/project-tree/gb-project-tree-private.h
rename to src/plugins/project-tree/gbp-project-tree-private.h
index cfa740317..ba0aa052d 100644
--- a/src/plugins/project-tree/gb-project-tree-private.h
+++ b/src/plugins/project-tree/gbp-project-tree-private.h
@@ -1,6 +1,6 @@
-/* gb-project-tree-private.h
+/* gbp-project-tree-private.h
*
- * Copyright 2015-2019 Christian Hergert <christian hergert me>
+ * Copyright 2018-2019 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
@@ -20,21 +20,21 @@
#pragma once
-#include <ide.h>
+#include <libide-gui.h>
+#include <libide-tree.h>
+
+#include "gbp-project-tree-pane.h"
G_BEGIN_DECLS
-struct _GbProjectTree
+struct _GbpProjectTreePane
{
- DzlTree parent_instance;
-
- GSettings *settings;
- PeasExtensionSet *addins;
-
- guint expanded_in_new : 1;
- guint show_ignored_files : 1;
+ IdePane parent_instance;
+ IdeTree *tree;
+ guint has_loaded : 1;
};
-void _gb_project_tree_init_shortcuts (GbProjectTree *self);
+void _gbp_project_tree_pane_init_actions (GbpProjectTreePane *self);
+void _gbp_project_tree_pane_update_actions (GbpProjectTreePane *self);
G_END_DECLS
diff --git a/src/plugins/project-tree/gbp-project-tree-workspace-addin.c
b/src/plugins/project-tree/gbp-project-tree-workspace-addin.c
new file mode 100644
index 000000000..394bf326a
--- /dev/null
+++ b/src/plugins/project-tree/gbp-project-tree-workspace-addin.c
@@ -0,0 +1,102 @@
+/* gbp-project-tree-workspace-addin.c
+ *
+ * Copyright 2018-2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "gbp-project-tree-workspace-addin"
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <libide-editor.h>
+#include <libide-gui.h>
+
+#include "gbp-project-tree-workspace-addin.h"
+#include "gbp-project-tree-pane.h"
+
+struct _GbpProjectTreeWorkspaceAddin
+{
+ GObject parent_instance;
+ GbpProjectTreePane *pane;
+};
+
+static void
+gbp_project_tree_workspace_addin_load (IdeWorkspaceAddin *addin,
+ IdeWorkspace *workspace)
+{
+ GbpProjectTreeWorkspaceAddin *self = (GbpProjectTreeWorkspaceAddin *)addin;
+ IdeEditorSidebar *sidebar;
+ IdeSurface *surface;
+
+ g_assert (GBP_IS_PROJECT_TREE_WORKSPACE_ADDIN (self));
+ g_assert (IDE_IS_PRIMARY_WORKSPACE (workspace));
+
+ surface = ide_workspace_get_surface_by_name (workspace, "editor");
+ g_assert (IDE_IS_EDITOR_SURFACE (surface));
+
+ sidebar = ide_editor_surface_get_sidebar (IDE_EDITOR_SURFACE (surface));
+ g_assert (IDE_IS_EDITOR_SIDEBAR (sidebar));
+
+ self->pane = g_object_new (GBP_TYPE_PROJECT_TREE_PANE,
+ "visible", TRUE,
+ NULL);
+ g_signal_connect (self->pane,
+ "destroy",
+ G_CALLBACK (gtk_widget_destroyed),
+ &self->pane);
+ ide_editor_sidebar_add_section (sidebar,
+ "project-tree",
+ _("Project Tree"),
+ "view-list-symbolic",
+ NULL, NULL,
+ GTK_WIDGET (self->pane),
+ 0);
+}
+
+static void
+gbp_project_tree_workspace_addin_unload (IdeWorkspaceAddin *addin,
+ IdeWorkspace *workspace)
+{
+ GbpProjectTreeWorkspaceAddin *self = (GbpProjectTreeWorkspaceAddin *)addin;
+
+ g_assert (GBP_IS_PROJECT_TREE_WORKSPACE_ADDIN (self));
+ g_assert (IDE_IS_PRIMARY_WORKSPACE (workspace));
+
+ if (self->pane != NULL)
+ gtk_widget_destroy (GTK_WIDGET (self->pane));
+}
+
+static void
+workspace_addin_iface_init (IdeWorkspaceAddinInterface *iface)
+{
+ iface->load = gbp_project_tree_workspace_addin_load;
+ iface->unload = gbp_project_tree_workspace_addin_unload;
+}
+
+G_DEFINE_TYPE_WITH_CODE (GbpProjectTreeWorkspaceAddin, gbp_project_tree_workspace_addin, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (IDE_TYPE_WORKSPACE_ADDIN, workspace_addin_iface_init))
+
+static void
+gbp_project_tree_workspace_addin_class_init (GbpProjectTreeWorkspaceAddinClass *klass)
+{
+}
+
+static void
+gbp_project_tree_workspace_addin_init (GbpProjectTreeWorkspaceAddin *self)
+{
+}
diff --git a/src/plugins/project-tree/gb-vcs-tree-builder.h
b/src/plugins/project-tree/gbp-project-tree-workspace-addin.h
similarity index 68%
rename from src/plugins/project-tree/gb-vcs-tree-builder.h
rename to src/plugins/project-tree/gbp-project-tree-workspace-addin.h
index 16fc64747..796a0163c 100644
--- a/src/plugins/project-tree/gb-vcs-tree-builder.h
+++ b/src/plugins/project-tree/gbp-project-tree-workspace-addin.h
@@ -1,6 +1,6 @@
-/* gb-vcs-tree-builder.h
+/* gbp-project-tree-workspace-addin.h
*
- * Copyright 2017-2019 Christian Hergert <chergert redhat com>
+ * Copyright 2018-2019 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
@@ -20,14 +20,12 @@
#pragma once
-#include <dazzle.h>
+#include <glib-object.h>
G_BEGIN_DECLS
-#define GB_TYPE_VCS_TREE_BUILDER (gb_vcs_tree_builder_get_type())
+#define GBP_TYPE_PROJECT_TREE_WORKSPACE_ADDIN (gbp_project_tree_workspace_addin_get_type())
-G_DECLARE_FINAL_TYPE (GbVcsTreeBuilder, gb_vcs_tree_builder, GB, VCS_TREE_BUILDER, DzlTreeBuilder)
-
-DzlTreeBuilder *gb_vcs_tree_builder_new (void);
+G_DECLARE_FINAL_TYPE (GbpProjectTreeWorkspaceAddin, gbp_project_tree_workspace_addin, GBP,
PROJECT_TREE_WORKSPACE_ADDIN, GObject)
G_END_DECLS
diff --git a/src/plugins/project-tree/gbp-project-tree.c b/src/plugins/project-tree/gbp-project-tree.c
new file mode 100644
index 000000000..5fd644118
--- /dev/null
+++ b/src/plugins/project-tree/gbp-project-tree.c
@@ -0,0 +1,178 @@
+/* gbp-project-tree.c
+ *
+ * Copyright 2018-2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "gbp-project-tree"
+
+#include "config.h"
+
+#include <libide-gui.h>
+#include <libide-projects.h>
+
+#include "gbp-project-tree.h"
+
+struct _GbpProjectTree
+{
+ IdeTree parent_instance;
+};
+
+G_DEFINE_TYPE (GbpProjectTree, gbp_project_tree, IDE_TYPE_TREE)
+
+static IdeTreeNodeVisit
+locate_project_files (IdeTreeNode *node,
+ gpointer user_data)
+{
+ IdeTreeNode **out_node = user_data;
+
+ if (ide_tree_node_holds (node, IDE_TYPE_PROJECT_FILE))
+ {
+ *out_node = node;
+ return IDE_TREE_NODE_VISIT_BREAK;
+ }
+
+ return IDE_TREE_NODE_VISIT_CONTINUE;
+}
+
+static void
+project_files_expanded_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeTreeModel *model = (IdeTreeModel *)object;
+ g_autoptr(IdeTask) task = user_data;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_TREE_MODEL (model));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_TASK (task));
+
+ if (ide_tree_model_expand_finish (model, result, NULL))
+ {
+ g_autoptr(GtkTreePath) path = NULL;
+ GbpProjectTree *self;
+ IdeTreeNode *node;
+
+ self = ide_task_get_source_object (task);
+ node = ide_task_get_task_data (task);
+
+ g_assert (GBP_IS_PROJECT_TREE (self));
+ g_assert (IDE_IS_TREE_NODE (node));
+
+ if ((path = ide_tree_node_get_path (node)))
+ gtk_tree_view_expand_row (GTK_TREE_VIEW (self), path, FALSE);
+ }
+
+ ide_task_return_boolean (task, TRUE);
+}
+
+static void
+gbp_project_tree_expand_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeTreeModel *model = (IdeTreeModel *)object;
+ g_autoptr(IdeTask) task = user_data;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_TREE_MODEL (model));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_TASK (task));
+
+ if (ide_tree_model_expand_finish (model, result, NULL))
+ {
+ IdeTreeNode *root = ide_tree_model_get_root (model);
+ IdeTreeNode *node = NULL;
+
+ ide_tree_node_traverse (root,
+ G_PRE_ORDER,
+ G_TRAVERSE_ALL,
+ 1,
+ locate_project_files,
+ &node);
+
+ if (node == NULL)
+ goto cleanup;
+
+ ide_task_set_task_data (task, g_object_ref (node), g_object_unref);
+
+ ide_tree_model_expand_async (model,
+ node,
+ NULL,
+ project_files_expanded_cb,
+ g_steal_pointer (&task));
+
+ return;
+ }
+
+cleanup:
+ ide_task_return_boolean (task, TRUE);
+}
+
+static void
+gbp_project_tree_hierarchy_changed (GtkWidget *widget,
+ GtkWidget *old_toplevel)
+{
+ GbpProjectTree *self = (GbpProjectTree *)widget;
+ GtkWidget *toplevel;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (GBP_IS_PROJECT_TREE (self));
+
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self));
+
+ if (IDE_IS_WORKSPACE (toplevel))
+ {
+ IdeContext *context = ide_widget_get_context (GTK_WIDGET (toplevel));
+ g_autoptr(IdeTreeNode) root = ide_tree_node_new ();
+ g_autoptr(IdeTreeModel) model = NULL;
+ g_autoptr(IdeTask) task = NULL;
+
+ model = g_object_new (IDE_TYPE_TREE_MODEL,
+ "kind", "project-tree",
+ "tree", self,
+ NULL);
+ gtk_tree_view_set_model (GTK_TREE_VIEW (self), GTK_TREE_MODEL (model));
+
+ ide_tree_node_set_item (root, context);
+ ide_object_append (IDE_OBJECT (context), IDE_OBJECT (model));
+ ide_tree_model_set_root (model, root);
+
+ task = ide_task_new (self, NULL, NULL, NULL);
+ ide_task_set_source_tag (task, gbp_project_tree_hierarchy_changed);
+
+ ide_tree_model_expand_async (model,
+ root,
+ NULL,
+ gbp_project_tree_expand_cb,
+ g_steal_pointer (&task));
+ }
+}
+
+static void
+gbp_project_tree_class_init (GbpProjectTreeClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ widget_class->hierarchy_changed = gbp_project_tree_hierarchy_changed;
+}
+
+static void
+gbp_project_tree_init (GbpProjectTree *self)
+{
+}
diff --git a/src/plugins/project-tree/gb-project-tree-actions.h b/src/plugins/project-tree/gbp-project-tree.h
similarity index 74%
rename from src/plugins/project-tree/gb-project-tree-actions.h
rename to src/plugins/project-tree/gbp-project-tree.h
index c63617954..1a1404dbe 100644
--- a/src/plugins/project-tree/gb-project-tree-actions.h
+++ b/src/plugins/project-tree/gbp-project-tree.h
@@ -1,6 +1,6 @@
-/* gb-project-tree-actions.h
+/* gbp-project-tree.h
*
- * Copyright 2015-2019 Christian Hergert <christian hergert me>
+ * Copyright 2018-2019 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
@@ -20,11 +20,12 @@
#pragma once
-#include "gb-project-tree.h"
+#include <libide-tree.h>
G_BEGIN_DECLS
-void gb_project_tree_actions_init (GbProjectTree *self);
-void gb_project_tree_actions_update (GbProjectTree *self);
+#define GBP_TYPE_PROJECT_TREE (gbp_project_tree_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpProjectTree, gbp_project_tree, GBP, PROJECT_TREE, IdeTree)
G_END_DECLS
diff --git a/src/plugins/project-tree/gb-rename-file-popover.c
b/src/plugins/project-tree/gbp-rename-file-popover.c
similarity index 58%
rename from src/plugins/project-tree/gb-rename-file-popover.c
rename to src/plugins/project-tree/gbp-rename-file-popover.c
index b26eec218..2453d000f 100644
--- a/src/plugins/project-tree/gb-rename-file-popover.c
+++ b/src/plugins/project-tree/gbp-rename-file-popover.c
@@ -1,4 +1,4 @@
-/* gb-rename-file-popover.c
+/* gbp-rename-file-popover.c
*
* Copyright 2015-2019 Christian Hergert <christian hergert me>
*
@@ -18,17 +18,20 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
+#define G_LOG_DOMAIN "gbp-rename-file-popover"
+
#include <glib/gi18n.h>
-#include <ide.h>
+#include <libide-gui.h>
-#include "gb-rename-file-popover.h"
+#include "gbp-rename-file-popover.h"
-struct _GbRenameFilePopover
+struct _GbpRenameFilePopover
{
GtkPopover parent_instance;
GCancellable *cancellable;
GFile *file;
+ IdeTask *task;
GtkEntry *entry;
GtkButton *button;
@@ -42,32 +45,32 @@ enum {
PROP_0,
PROP_FILE,
PROP_IS_DIRECTORY,
- LAST_PROP
+ N_PROPS
};
enum {
RENAME_FILE,
- LAST_SIGNAL
+ N_SIGNALS
};
-G_DEFINE_TYPE (GbRenameFilePopover, gb_rename_file_popover, GTK_TYPE_POPOVER)
+G_DEFINE_TYPE (GbpRenameFilePopover, gbp_rename_file_popover, GTK_TYPE_POPOVER)
-static GParamSpec *properties [LAST_PROP];
-static guint signals [LAST_SIGNAL];
+static GParamSpec *properties [N_PROPS];
+static guint signals [N_SIGNALS];
GFile *
-gb_rename_file_popover_get_file (GbRenameFilePopover *self)
+gbp_rename_file_popover_get_file (GbpRenameFilePopover *self)
{
- g_return_val_if_fail (GB_IS_RENAME_FILE_POPOVER (self), NULL);
+ g_return_val_if_fail (GBP_IS_RENAME_FILE_POPOVER (self), NULL);
return self->file;
}
static void
-gb_rename_file_popover_set_file (GbRenameFilePopover *self,
+gbp_rename_file_popover_set_file (GbpRenameFilePopover *self,
GFile *file)
{
- g_return_if_fail (GB_IS_RENAME_FILE_POPOVER (self));
+ g_return_if_fail (GBP_IS_RENAME_FILE_POPOVER (self));
g_return_if_fail (G_IS_FILE (file));
if (g_set_object (&self->file, file))
@@ -89,10 +92,10 @@ gb_rename_file_popover_set_file (GbRenameFilePopover *self,
}
static void
-gb_rename_file_popover_set_is_directory (GbRenameFilePopover *self,
+gbp_rename_file_popover_set_is_directory (GbpRenameFilePopover *self,
gboolean is_directory)
{
- g_return_if_fail (GB_IS_RENAME_FILE_POPOVER (self));
+ g_return_if_fail (GBP_IS_RENAME_FILE_POPOVER (self));
is_directory = !!is_directory;
@@ -104,13 +107,13 @@ gb_rename_file_popover_set_is_directory (GbRenameFilePopover *self,
}
static void
-gb_rename_file_popover__file_query_info (GObject *object,
+gbp_rename_file_popover__file_query_info (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
GFile *file = (GFile *)object;
g_autoptr(GFileInfo) file_info = NULL;
- g_autoptr(GbRenameFilePopover) self = user_data;
+ g_autoptr(GbpRenameFilePopover) self = user_data;
g_autoptr(GError) error = NULL;
GFileType file_type;
@@ -147,14 +150,14 @@ gb_rename_file_popover__file_query_info (GObject *object,
}
static void
-gb_rename_file_popover__entry_changed (GbRenameFilePopover *self,
+gbp_rename_file_popover__entry_changed (GbpRenameFilePopover *self,
GtkEntry *entry)
{
g_autoptr(GFile) parent = NULL;
g_autoptr(GFile) file = NULL;
const gchar *text;
- g_assert (GB_IS_RENAME_FILE_POPOVER (self));
+ g_assert (GBP_IS_RENAME_FILE_POPOVER (self));
g_assert (GTK_IS_ENTRY (entry));
g_assert (self->file != NULL);
g_assert (G_IS_FILE (self->file));
@@ -163,7 +166,7 @@ gb_rename_file_popover__entry_changed (GbRenameFilePopover *self,
gtk_label_set_label (self->message, NULL);
text = gtk_entry_get_text (entry);
- if (dzl_str_empty0 (text))
+ if (ide_str_empty0 (text))
return;
if (self->cancellable)
@@ -182,15 +185,15 @@ gb_rename_file_popover__entry_changed (GbRenameFilePopover *self,
G_FILE_QUERY_INFO_NONE,
G_PRIORITY_DEFAULT,
self->cancellable,
- gb_rename_file_popover__file_query_info,
+ gbp_rename_file_popover__file_query_info,
g_object_ref (self));
}
static void
-gb_rename_file_popover__entry_activate (GbRenameFilePopover *self,
+gbp_rename_file_popover__entry_activate (GbpRenameFilePopover *self,
GtkEntry *entry)
{
- g_assert (GB_IS_RENAME_FILE_POPOVER (self));
+ g_assert (GBP_IS_RENAME_FILE_POPOVER (self));
g_assert (GTK_IS_ENTRY (entry));
if (gtk_widget_get_sensitive (GTK_WIDGET (self->button)))
@@ -198,14 +201,14 @@ gb_rename_file_popover__entry_activate (GbRenameFilePopover *self,
}
static void
-gb_rename_file_popover__entry_focus_in_event (GbRenameFilePopover *self,
+gbp_rename_file_popover__entry_focus_in_event (GbpRenameFilePopover *self,
GdkEvent *event,
GtkEntry *entry)
{
const gchar *name;
const gchar *tmp;
- g_assert (GB_IS_RENAME_FILE_POPOVER (self));
+ g_assert (GBP_IS_RENAME_FILE_POPOVER (self));
g_assert (GTK_IS_ENTRY (entry));
name = gtk_entry_get_text (entry);
@@ -215,20 +218,21 @@ gb_rename_file_popover__entry_focus_in_event (GbRenameFilePopover *self,
}
static void
-gb_rename_file_popover__button_clicked (GbRenameFilePopover *self,
+gbp_rename_file_popover__button_clicked (GbpRenameFilePopover *self,
GtkButton *button)
{
g_autoptr(GFile) file = NULL;
g_autoptr(GFile) parent = NULL;
+ g_autoptr(IdeTask) task = NULL;
const gchar *path;
- g_assert (GB_IS_RENAME_FILE_POPOVER (self));
+ g_assert (GBP_IS_RENAME_FILE_POPOVER (self));
g_assert (GTK_IS_BUTTON (button));
g_assert (self->file != NULL);
g_assert (G_IS_FILE (self->file));
path = gtk_entry_get_text (self->entry);
- if (dzl_str_empty0 (path))
+ if (ide_str_empty0 (path))
return;
parent = g_file_get_parent (self->file);
@@ -238,12 +242,32 @@ gb_rename_file_popover__button_clicked (GbRenameFilePopover *self,
gtk_widget_set_sensitive (GTK_WIDGET (self->button), FALSE);
g_signal_emit (self, signals [RENAME_FILE], 0, self->file, file);
+
+ /* Complete our async op */
+ if ((task = g_steal_pointer (&self->task)))
+ ide_task_return_pointer (task, g_steal_pointer (&file), g_object_unref);
}
static void
-gb_rename_file_popover_finalize (GObject *object)
+gbp_rename_file_popover_closed (GtkPopover *popover)
{
- GbRenameFilePopover *self = (GbRenameFilePopover *)object;
+ g_autoptr(IdeTask) task = NULL;
+ GbpRenameFilePopover *self = (GbpRenameFilePopover *)popover;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (GBP_IS_RENAME_FILE_POPOVER (self));
+
+ if ((task = g_steal_pointer (&self->task)))
+ ide_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_CANCELLED,
+ "The popover was cancelled");
+}
+
+static void
+gbp_rename_file_popover_finalize (GObject *object)
+{
+ GbpRenameFilePopover *self = (GbpRenameFilePopover *)object;
if (self->cancellable != NULL)
{
@@ -254,16 +278,18 @@ gb_rename_file_popover_finalize (GObject *object)
g_clear_object (&self->file);
- G_OBJECT_CLASS (gb_rename_file_popover_parent_class)->finalize (object);
+ g_assert (self->task == NULL);
+
+ G_OBJECT_CLASS (gbp_rename_file_popover_parent_class)->finalize (object);
}
static void
-gb_rename_file_popover_get_property (GObject *object,
+gbp_rename_file_popover_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
- GbRenameFilePopover *self = GB_RENAME_FILE_POPOVER (object);
+ GbpRenameFilePopover *self = GBP_RENAME_FILE_POPOVER (object);
switch (prop_id)
{
@@ -281,21 +307,21 @@ gb_rename_file_popover_get_property (GObject *object,
}
static void
-gb_rename_file_popover_set_property (GObject *object,
+gbp_rename_file_popover_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
- GbRenameFilePopover *self = GB_RENAME_FILE_POPOVER (object);
+ GbpRenameFilePopover *self = GBP_RENAME_FILE_POPOVER (object);
switch (prop_id)
{
case PROP_FILE:
- gb_rename_file_popover_set_file (self, g_value_get_object (value));
+ gbp_rename_file_popover_set_file (self, g_value_get_object (value));
break;
case PROP_IS_DIRECTORY:
- gb_rename_file_popover_set_is_directory (self, g_value_get_boolean (value));
+ gbp_rename_file_popover_set_is_directory (self, g_value_get_boolean (value));
break;
default:
@@ -304,14 +330,17 @@ gb_rename_file_popover_set_property (GObject *object,
}
static void
-gb_rename_file_popover_class_init (GbRenameFilePopoverClass *klass)
+gbp_rename_file_popover_class_init (GbpRenameFilePopoverClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GtkPopoverClass *popover_class = GTK_POPOVER_CLASS (klass);
+
+ object_class->finalize = gbp_rename_file_popover_finalize;
+ object_class->get_property = gbp_rename_file_popover_get_property;
+ object_class->set_property = gbp_rename_file_popover_set_property;
- object_class->finalize = gb_rename_file_popover_finalize;
- object_class->get_property = gb_rename_file_popover_get_property;
- object_class->set_property = gb_rename_file_popover_set_property;
+ popover_class->closed = gbp_rename_file_popover_closed;
properties [PROP_FILE] =
g_param_spec_object ("file",
@@ -327,7 +356,7 @@ gb_rename_file_popover_class_init (GbRenameFilePopoverClass *klass)
FALSE,
(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
- g_object_class_install_properties (object_class, LAST_PROP, properties);
+ g_object_class_install_properties (object_class, N_PROPS, properties);
signals [RENAME_FILE] =
g_signal_new ("rename-file",
@@ -340,39 +369,84 @@ gb_rename_file_popover_class_init (GbRenameFilePopoverClass *klass)
G_TYPE_FILE,
G_TYPE_FILE);
- gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/builder/plugins/project-tree-plugin/gb-rename-file-popover.ui");
- gtk_widget_class_bind_template_child (widget_class, GbRenameFilePopover, button);
- gtk_widget_class_bind_template_child (widget_class, GbRenameFilePopover, entry);
- gtk_widget_class_bind_template_child (widget_class, GbRenameFilePopover, label);
- gtk_widget_class_bind_template_child (widget_class, GbRenameFilePopover, message);
+ gtk_widget_class_set_template_from_resource (widget_class,
"/plugins/project-tree/gbp-rename-file-popover.ui");
+ gtk_widget_class_bind_template_child (widget_class, GbpRenameFilePopover, button);
+ gtk_widget_class_bind_template_child (widget_class, GbpRenameFilePopover, entry);
+ gtk_widget_class_bind_template_child (widget_class, GbpRenameFilePopover, label);
+ gtk_widget_class_bind_template_child (widget_class, GbpRenameFilePopover, message);
}
static void
-gb_rename_file_popover_init (GbRenameFilePopover *self)
+gbp_rename_file_popover_init (GbpRenameFilePopover *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
g_signal_connect_object (self->entry,
"changed",
- G_CALLBACK (gb_rename_file_popover__entry_changed),
+ G_CALLBACK (gbp_rename_file_popover__entry_changed),
self,
G_CONNECT_SWAPPED);
g_signal_connect_object (self->entry,
"activate",
- G_CALLBACK (gb_rename_file_popover__entry_activate),
+ G_CALLBACK (gbp_rename_file_popover__entry_activate),
self,
G_CONNECT_SWAPPED);
g_signal_connect_object (self->button,
"clicked",
- G_CALLBACK (gb_rename_file_popover__button_clicked),
+ G_CALLBACK (gbp_rename_file_popover__button_clicked),
self,
G_CONNECT_SWAPPED);
g_signal_connect_object (self->entry,
"focus-in-event",
- G_CALLBACK (gb_rename_file_popover__entry_focus_in_event),
+ G_CALLBACK (gbp_rename_file_popover__entry_focus_in_event),
self,
G_CONNECT_SWAPPED | G_CONNECT_AFTER);
}
+
+void
+gbp_rename_file_popover_display_async (GbpRenameFilePopover *self,
+ IdeTree *tree,
+ IdeTreeNode *node,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(IdeTask) task = NULL;
+
+ g_return_if_fail (IDE_IS_MAIN_THREAD ());
+ g_return_if_fail (GBP_IS_RENAME_FILE_POPOVER (self));
+ g_return_if_fail (IDE_IS_TREE (tree));
+ g_return_if_fail (IDE_IS_TREE_NODE (node));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = ide_task_new (self, cancellable, callback, user_data);
+ ide_task_set_source_tag (task, gbp_rename_file_popover_display_async);
+
+ if (self->task != NULL)
+ {
+ ide_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "Already displayed popover");
+ return;
+ }
+
+ self->task = g_steal_pointer (&task);
+
+ ide_tree_show_popover_at_node (tree, node, GTK_POPOVER (self));
+}
+
+GFile *
+gbp_rename_file_popover_display_finish (GbpRenameFilePopover *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
+ g_return_val_if_fail (GBP_IS_RENAME_FILE_POPOVER (self), NULL);
+ g_return_val_if_fail (IDE_IS_TASK (result), NULL);
+
+ return ide_task_propagate_pointer (IDE_TASK (result), error);
+}
diff --git a/src/plugins/project-tree/gbp-rename-file-popover.h
b/src/plugins/project-tree/gbp-rename-file-popover.h
new file mode 100644
index 000000000..a7897e56d
--- /dev/null
+++ b/src/plugins/project-tree/gbp-rename-file-popover.h
@@ -0,0 +1,42 @@
+/* gbp-rename-file-popover.h
+ *
+ * Copyright 2015-2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <libide-tree.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_RENAME_FILE_POPOVER (gbp_rename_file_popover_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpRenameFilePopover, gbp_rename_file_popover, GBP, RENAME_FILE_POPOVER, GtkPopover)
+
+GFile *gbp_rename_file_popover_get_file (GbpRenameFilePopover *self);
+void gbp_rename_file_popover_display_async (GbpRenameFilePopover *self,
+ IdeTree *tree,
+ IdeTreeNode *node,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GFile *gbp_rename_file_popover_display_finish (GbpRenameFilePopover *self,
+ GAsyncResult *result,
+ GError **error);
+
+G_END_DECLS
diff --git a/src/plugins/project-tree/gb-rename-file-popover.ui
b/src/plugins/project-tree/gbp-rename-file-popover.ui
similarity index 97%
rename from src/plugins/project-tree/gb-rename-file-popover.ui
rename to src/plugins/project-tree/gbp-rename-file-popover.ui
index dbdb52d40..9da58ddc6 100644
--- a/src/plugins/project-tree/gb-rename-file-popover.ui
+++ b/src/plugins/project-tree/gbp-rename-file-popover.ui
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<!-- interface-requires gtk+ 3.16 -->
- <template class="GbRenameFilePopover" parent="GtkPopover">
+ <template class="GbpRenameFilePopover" parent="GtkPopover">
<child>
<object class="GtkBox">
<property name="border-width">12</property>
diff --git a/src/plugins/project-tree/gtk/menus.ui b/src/plugins/project-tree/gtk/menus.ui
index 903289782..519e139ef 100644
--- a/src/plugins/project-tree/gtk/menus.ui
+++ b/src/plugins/project-tree/gtk/menus.ui
@@ -1,107 +1,85 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
<interface>
- <menu id="ide-source-view-popup-menu">
- <section id="ide-source-view-popup-menu-reveal-section">
+ <menu id="project-tree-menu">
+ <section id="project-tree-menu-placeholder0"/>
+ <section id="project-tree-menu-new-section">
<item>
- <attribute name="label" translatable="yes">Re_veal in Project Tree</attribute>
- <attribute name="action">project-tree.reveal</attribute>
- </item>
- </section>
- </menu>
- <menu id="gb-project-tree-popup-menu">
- <section id="gb-project-tree-new-section">
- <item>
- <attribute name="label" translatable="yes">New _File</attribute>
+ <attribute name="id">project-tree-menu-new-file</attribute>
+ <attribute name="label" translatable="yes">New File…</attribute>
<attribute name="action">project-tree.new-file</attribute>
</item>
<item>
- <attribute name="label" translatable="yes">_New Folder</attribute>
- <attribute name="action">project-tree.new-directory</attribute>
+ <attribute name="id">project-tree-menu-new-folder</attribute>
+ <attribute name="label" translatable="yes">New Folder…</attribute>
+ <attribute name="action">project-tree.new-folder</attribute>
</item>
</section>
- <section id="gb-project-tree-open-section">
+ <section id="project-tree-menu-placeholder1"/>
+ <section id="project-tree-menu-open-section">
<item>
- <attribute name="label" translatable="yes">_Open</attribute>
+ <attribute name="id">project-tree-menu-open</attribute>
+ <attribute name="label" translatable="yes">Open</attribute>
<attribute name="action">project-tree.open</attribute>
- <attribute name="accel">Return</attribute>
</item>
- <submenu id="gb-project-tree-open-with-submenu">
- <attribute name="label" translatable="yes">Open _With</attribute>
- <section id="gb-project-tree-open-with-internal-section">
+ <submenu id="project-tree-menu-open-with-menu">
+ <attribute name="label" translatable="yes">Open With…</attribute>
+ <section id="project-tree-menu-open-with-section">
<item>
- <attribute name="id">gb-project-tree-open-with-editor</attribute>
+ <attribute name="id">project-tree-menu-open-editor</attribute>
<attribute name="label" translatable="yes">Source Code Editor</attribute>
<attribute name="action">project-tree.open-with-hint</attribute>
- <attribute name="target" type="s">"editor"</attribute>
+ <attribute name="target" type="s">'editor'</attribute>
+ </item>
+ <item>
+ <attribute name="id">project-tree-menu-open-editor</attribute>
+ <attribute name="label" translatable="yes">UI Designer</attribute>
+ <attribute name="action">project-tree.open-with-hint</attribute>
+ <attribute name="target" type="s">'glade'</attribute>
</item>
- </section>
- <section id="gb-project-tree-open-by-mime-section">
</section>
</submenu>
- </section>
- <section id="gb-project-tree-open-containing-section">
<item>
- <attribute name="label" translatable="yes">_Open Containing Folder</attribute>
+ <attribute name="id">project-tree-menu-open-folder</attribute>
+ <attribute name="label" translatable="yes">Open Containing Folder</attribute>
<attribute name="action">project-tree.open-containing-folder</attribute>
</item>
<item>
- <attribute name="label" translatable="yes">_Open in Terminal</attribute>
+ <attribute name="id">project-tree-menu-open-terminal</attribute>
+ <attribute name="label" translatable="yes">Open in Terminal</attribute>
<attribute name="action">project-tree.open-in-terminal</attribute>
</item>
</section>
- <section id="gb-project-tree-find-section"/>
- <section id="gb-project-tree-rename-section">
- <item>
- <attribute name="label" translatable="yes">_Rename</attribute>
- <attribute name="action">project-tree.rename-file</attribute>
- <attribute name="accel">F2</attribute>
- </item>
- </section>
- <section id="gb-project-tree-move-to-trash-section">
- <item>
- <attribute name="label" translatable="yes">Mo_ve to Trash</attribute>
- <attribute name="action">project-tree.move-to-trash</attribute>
- <attribute name="accel">Delete</attribute>
- </item>
- </section>
- <section id="gb-project-tree-build-section">
+ <section id="project-tree-menu-placeholder2"/>
+ <section id="project-tree-menu-destructive-section">
<item>
- <attribute name="label" translatable="yes">_Build</attribute>
- <attribute name="action">workbench.build</attribute>
+ <attribute name="id">project-tree-menu-rename</attribute>
+ <attribute name="label" translatable="yes">Rename</attribute>
+ <attribute name="action">project-tree.rename</attribute>
</item>
<item>
- <attribute name="label" translatable="yes">_Rebuild</attribute>
- <attribute name="action">workbench.rebuild</attribute>
+ <attribute name="id">project-tree-menu-trash</attribute>
+ <attribute name="label" translatable="yes">Move to Trash</attribute>
+ <attribute name="action">project-tree.trash</attribute>
</item>
</section>
- <section id="gb-project-tree-display-options-section">
- <submenu id="gb-project-tree-display-options-submenu">
+ <section id="project-tree-menu-placeholder3"/>
+ <section id="project-tree-menu-display-options-parent-section">
+ <submenu id="project-tree-menu-display-options">
<attribute name="label" translatable="yes">Display Options</attribute>
- <section id="gb-project-tree-display-options-show-section">
- <item>
- <attribute name="label" translatable="yes">Show Icons</attribute>
- <attribute name="action">project-tree.show-icons</attribute>
- </item>
+ <section id="project-tree-menu-display-options-section">
<item>
+ <attribute name="id">project-tree-menu-show-ignored</attribute>
<attribute name="label" translatable="yes">Show Ignored Files</attribute>
<attribute name="action">project-tree.show-ignored-files</attribute>
</item>
<item>
+ <attribute name="id">project-tree-menu-sort-directories-first</attribute>
<attribute name="label" translatable="yes">Sort Directories First</attribute>
<attribute name="action">project-tree.sort-directories-first</attribute>
</item>
</section>
- <section id="gb-project-tree-display-options-nodes-section">
- <item>
- <attribute name="label" translatable="yes">_Collapse All Nodes</attribute>
- <attribute name="action">project-tree.collapse-all-nodes</attribute>
- </item>
- <item>
- <attribute name="label" translatable="yes">_Refresh</attribute>
- <attribute name="action">project-tree.refresh</attribute>
- </item>
- </section>
</submenu>
</section>
+ <section id="project-tree-menu-placeholder4"/>
</menu>
</interface>
diff --git a/src/plugins/project-tree/meson.build b/src/plugins/project-tree/meson.build
index 4ca024259..c95aa92be 100644
--- a/src/plugins/project-tree/meson.build
+++ b/src/plugins/project-tree/meson.build
@@ -1,39 +1,18 @@
-if get_option('with_project_tree')
+plugins_sources += files([
+ 'project-tree-plugin.c',
+ 'gbp-project-tree.c',
+ 'gbp-project-tree-addin.c',
+ 'gbp-project-tree-pane.c',
+ 'gbp-project-tree-pane-actions.c',
+ 'gbp-project-tree-workspace-addin.c',
+ 'gbp-new-file-popover.c',
+ 'gbp-rename-file-popover.c',
+])
-project_tree_resources = gnome.compile_resources(
- 'project-tree-resources',
+plugin_project_tree_resources = gnome.compile_resources(
+ 'gbp-project-tree-resources',
'project-tree.gresource.xml',
- c_name: 'gb_project_tree',
+ c_name: 'gbp_project_tree',
)
-project_tree_sources = [
- 'gb-new-file-popover.c',
- 'gb-new-file-popover.h',
- 'gb-project-file.c',
- 'gb-project-file.h',
- 'gb-project-tree-actions.c',
- 'gb-project-tree-actions.h',
- 'gb-project-tree-builder.c',
- 'gb-project-tree-builder.h',
- 'gb-project-tree.c',
- 'gb-project-tree.h',
- 'gb-project-tree-editor-addin.c',
- 'gb-project-tree-editor-addin.h',
- 'gb-project-tree-private.h',
- 'gb-project-tree-shortcuts.c',
- 'gb-rename-file-popover.c',
- 'gb-rename-file-popover.h',
- 'gb-project-tree-addin.c',
- 'gb-project-tree-addin.h',
- 'gb-vcs-tree-builder.c',
- 'gb-vcs-tree-builder.h',
- 'project-tree-plugin.c',
-]
-
-gnome_builder_plugins_deps += dependency('vte-2.91', version: '>=0.40.2')
-gnome_builder_plugins_args += '-DHAVE_VTE'
-
-gnome_builder_plugins_sources += files(project_tree_sources)
-gnome_builder_plugins_sources += project_tree_resources[0]
-
-endif
+plugins_sources += plugin_project_tree_resources[0]
diff --git a/src/plugins/project-tree/project-tree-plugin.c b/src/plugins/project-tree/project-tree-plugin.c
index 97f7aa38a..1221ebadd 100644
--- a/src/plugins/project-tree/project-tree-plugin.c
+++ b/src/plugins/project-tree/project-tree-plugin.c
@@ -18,19 +18,24 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
+#define G_LOG_DOMAIN "project-tree-plugin"
+
+#include "config.h"
+
+#include <libide-gui.h>
+#include <libide-tree.h>
#include <libpeas/peas.h>
-#include <ide.h>
-#include "gb-project-tree-addin.h"
-#include "gb-project-tree-editor-addin.h"
+#include "gbp-project-tree-addin.h"
+#include "gbp-project-tree-workspace-addin.h"
void
-gb_project_tree_register_types (PeasObjectModule *module)
+_gbp_project_tree_register_types (PeasObjectModule *module)
{
peas_object_module_register_extension_type (module,
- IDE_TYPE_WORKBENCH_ADDIN,
- GB_TYPE_PROJECT_TREE_ADDIN);
+ IDE_TYPE_TREE_ADDIN,
+ GBP_TYPE_PROJECT_TREE_ADDIN);
peas_object_module_register_extension_type (module,
- IDE_TYPE_EDITOR_VIEW_ADDIN,
- GB_TYPE_PROJECT_TREE_EDITOR_ADDIN);
+ IDE_TYPE_WORKSPACE_ADDIN,
+ GBP_TYPE_PROJECT_TREE_WORKSPACE_ADDIN);
}
diff --git a/src/plugins/project-tree/project-tree.gresource.xml
b/src/plugins/project-tree/project-tree.gresource.xml
index 0c6b165b8..075b14326 100644
--- a/src/plugins/project-tree/project-tree.gresource.xml
+++ b/src/plugins/project-tree/project-tree.gresource.xml
@@ -1,12 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
- <gresource prefix="/org/gnome/builder/plugins">
+ <gresource prefix="/plugins/project-tree">
+ <file preprocess="xml-stripblanks">gtk/menus.ui</file>
+ <file preprocess="xml-stripblanks">gbp-project-tree-pane.ui</file>
+ <file preprocess="xml-stripblanks">gbp-new-file-popover.ui</file>
+ <file preprocess="xml-stripblanks">gbp-rename-file-popover.ui</file>
<file>project-tree.plugin</file>
- </gresource>
- <gresource prefix="/org/gnome/builder/plugins/project-tree-plugin">
- <file>gb-new-file-popover.ui</file>
- <file>gb-rename-file-popover.ui</file>
- <file>gtk/menus.ui</file>
<file>themes/shared.css</file>
</gresource>
</gresources>
diff --git a/src/plugins/project-tree/project-tree.plugin b/src/plugins/project-tree/project-tree.plugin
index c8dd44a06..8e7566092 100644
--- a/src/plugins/project-tree/project-tree.plugin
+++ b/src/plugins/project-tree/project-tree.plugin
@@ -1,10 +1,12 @@
[Plugin]
-Module=project-tree-plugin
-Name=Project Tree
-Description=Provides a project tree
Authors=Christian Hergert <christian hergert me>
-Copyright=Copyright © 2015 Christian Hergert
-Depends=editor
Builtin=true
+Copyright=Copyright © 2014-2018 Christian Hergert
+Depends=editor;
+Description=Builder's project creation wizard
+Embedded=_gbp_project_tree_register_types
Hidden=true
-Embedded=gb_project_tree_register_types
+Module=project-tree
+Name=Project Tree
+X-Workspace-Kind=primary;
+X-Tree-Kind=project-tree;
diff --git a/src/plugins/project-tree/themes/shared.css b/src/plugins/project-tree/themes/shared.css
index 79880fb6d..a6c2bd476 100644
--- a/src/plugins/project-tree/themes/shared.css
+++ b/src/plugins/project-tree/themes/shared.css
@@ -5,3 +5,11 @@ ideeditorsidebar treeview.project-tree {
-gtk-icon-source: none;
padding-left: 6px;
}
+
+.vcs-added {
+ color: @success_color;
+}
+
+.vcs-changed {
+ color: @warning_color;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]