[gnome-builder/wip/chergert/perspective] libide: more libide, more interfaces, get rid of most of src/
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/chergert/perspective] libide: more libide, more interfaces, get rid of most of src/
- Date: Wed, 4 Nov 2015 09:48:18 +0000 (UTC)
commit 149bb74115500e03551fef3aa9fee1400096b8c0
Author: Christian Hergert <chergert redhat com>
Date: Wed Nov 4 01:48:02 2015 -0800
libide: more libide, more interfaces, get rid of most of src/
contrib/egg/Makefile.am | 18 +
contrib/egg/egg-date-time.c | 70 ++
contrib/egg/egg-date-time.h | 30 +
.../egg/egg-pill-box.c | 60 +-
.../egg/egg-pill-box.h | 20 +-
.../egg/egg-pill-box.ui | 4 +-
contrib/egg/egg-scrolled-window.c | 280 ++++++
.../egg/egg-scrolled-window.h | 24 +-
contrib/egg/egg.gresource.xml | 6 +
data/theme/Adwaita-dark.css | 6 +
data/theme/Adwaita.css | 2 +-
data/theme/shared.css | 32 +-
data/ui/ide-editor-perspective.ui | 73 ++
data/ui/ide-greeter-perspective.ui | 281 ++++++
data/ui/ide-greeter-project-row.ui | 96 ++
data/ui/ide-workbench-header-bar.ui | 35 +
data/ui/ide-workbench.ui | 53 +
libide/Makefile.am | 43 +-
libide/editor/ide-editor-perspective.c | 121 +++
.../editor/ide-editor-perspective.h | 21 +-
libide/greeter/ide-greeter-perspective.c | 699 ++++++++++++++
.../greeter/ide-greeter-perspective.h | 19 +-
libide/greeter/ide-greeter-project-row.c | 350 +++++++
.../greeter/ide-greeter-project-row.h | 22 +-
libide/ide-application-actions.c | 317 ++++++
.../ide-application-actions.h | 19 +-
libide/ide-application-addin.c | 81 ++
libide/ide-application-addin.h | 47 +
libide/ide-application-credits.h | 554 +++++++++++
.../ide-application-private.h | 37 +-
libide/ide-application.c | 1002 ++++++++++++++++++++
libide/ide-application.h | 57 ++
libide/ide-css-provider.c | 176 ++++
.../ide-css-provider.h | 17 +-
libide/ide-internal.h | 2 +-
libide/ide-keybindings.c | 282 ++++++
libide/ide-keybindings.h | 39 +
libide/ide-layout-manager.c | 72 ++
libide/ide-layout-manager.h | 60 ++
libide/ide-perspective.c | 263 +++++
libide/ide-perspective.h | 63 ++
libide/ide-preferences-addin.c | 81 ++
libide/ide-preferences-addin.h | 49 +
libide/ide-preferences.c | 116 +++
libide/ide-preferences.h | 105 ++
libide/ide-thread-pool.c | 21 +-
libide/ide-view.c | 129 +++
libide/ide-view.h | 61 ++
libide/ide-workbench-addin.c | 83 ++
libide/ide-workbench-addin.h | 47 +
libide/ide-workbench-header-bar.c | 58 ++
.../ide-workbench-header-bar.h | 24 +-
libide/ide-workbench-private.h | 59 ++
libide/ide-workbench.c | 396 ++++++++
libide/ide-workbench.h | 69 ++
libide/ide.c | 36 -
libide/ide.h | 6 +
libide/resources/libide.gresource.xml | 9 +
libide/util/ide-window-settings.c | 154 +++
.../util/ide-window-settings.h | 17 +-
src/Makefile.am | 2 -
src/greeter/gb-greeter-project-row.c | 4 +-
src/main.c | 3 +-
src/resources/gnome-builder.gresource.xml | 1 -
src/util/gb-glib.h | 2 -
65 files changed, 6784 insertions(+), 201 deletions(-)
---
diff --git a/contrib/egg/Makefile.am b/contrib/egg/Makefile.am
index 3c0f260..7ca0a1f 100644
--- a/contrib/egg/Makefile.am
+++ b/contrib/egg/Makefile.am
@@ -1,6 +1,7 @@
CLEANFILES =
DISTCLEANFILES =
EXTRA_DIST =
+BUILT_SOURCES =
pkglibdir = $(libdir)/gnome-builder
pkglib_LTLIBRARIES = libegg-private.la
@@ -12,11 +13,17 @@ libegg_private_la_SOURCES = \
egg-binding-group.h \
egg-counter.c \
egg-counter.h \
+ egg-date-time.c \
+ egg-date-time.h \
egg-frame-source.c \
egg-frame-source.h \
egg-heap.c \
egg-heap.h \
+ egg-pill-box.c \
+ egg-pill-box.h \
egg-private.h \
+ egg-scrolled-window.c \
+ egg-scrolled-window.h \
egg-search-bar.c \
egg-search-bar.h \
egg-settings-flag-action.c \
@@ -35,6 +42,17 @@ libegg_private_la_SOURCES = \
egg-task-cache.h \
$(NULL)
+nodist_libegg_private_la_SOURCES = \
+ egg-resources.c \
+ egg-resources.h \
+ $(NULL)
+
+glib_resources_c = egg-resources.c
+glib_resources_h = egg-resources.h
+glib_resources_xml = egg.gresource.xml
+glib_resources_namespace = egg
+include $(top_srcdir)/build/autotools/Makefile.am.gresources
+
libegg_private_la_CFLAGS = \
$(EGG_CFLAGS) \
$(NULL)
diff --git a/contrib/egg/egg-date-time.c b/contrib/egg/egg-date-time.c
new file mode 100644
index 0000000..acf3918
--- /dev/null
+++ b/contrib/egg/egg-date-time.c
@@ -0,0 +1,70 @@
+/* egg-date-time.c
+ *
+ * Copyright (C) 2015 Christian Hergert <chergert redhat com>
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This file 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n.h>
+
+#include "egg-date-time.h"
+
+/**
+ * egg_date_time_format_for_display:
+ * @self: A #GDateTime
+ *
+ * Helper function to "humanize" a #GDateTime into a relative time
+ * relationship string.
+ *
+ * Returns: (transfer full): A newly allocated string describing the
+ * date and time imprecisely such as "Yesterday".
+ */
+gchar *
+egg_date_time_format_for_display (GDateTime *self)
+{
+ GDateTime *now;
+ GTimeSpan diff;
+ gint years;
+
+ /*
+ * TODO:
+ *
+ * There is probably a lot more we can do here to be friendly for
+ * various locales, but this will get us started.
+ */
+
+ g_return_val_if_fail (self != NULL, NULL);
+
+ now = g_date_time_new_now_utc ();
+ diff = g_date_time_difference (now, self) / G_USEC_PER_SEC;
+
+ if (diff < 0)
+ return g_strdup ("");
+ else if (diff < (60 * 45))
+ return g_strdup (_("Just now"));
+ else if (diff < (60 * 90))
+ return g_strdup (_("An hour ago"));
+ else if (diff < (60 * 60 * 24 * 2))
+ return g_strdup (_("Yesterday"));
+ else if (diff < (60 * 60 * 24 * 7))
+ return g_date_time_format (self, "%A");
+ else if (diff < (60 * 60 * 24 * 365))
+ return g_date_time_format (self, "%B");
+ else if (diff < (60 * 60 * 24 * 365 * 1.5))
+ return g_strdup (_("About a year ago"));
+
+ years = MAX (2, diff / (60 * 60 * 24 * 365));
+
+ return g_strdup_printf (ngettext ("About %u year ago", "About %u years ago", years), years);
+}
diff --git a/contrib/egg/egg-date-time.h b/contrib/egg/egg-date-time.h
new file mode 100644
index 0000000..635f3f9
--- /dev/null
+++ b/contrib/egg/egg-date-time.h
@@ -0,0 +1,30 @@
+/* egg-date-time.h
+ *
+ * Copyright (C) 2015 Christian Hergert <chergert redhat com>
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This file 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
+ * Lesser 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/>.
+ */
+
+#ifndef EGG_DATE_TIME_H
+#define EGG_DATE_TIME_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+gchar *egg_date_time_format_for_display (GDateTime *self);
+
+G_END_DECLS
+
+#endif /* EGG_DATE_TIME_H */
diff --git a/src/greeter/gb-greeter-pill-box.c b/contrib/egg/egg-pill-box.c
similarity index 55%
rename from src/greeter/gb-greeter-pill-box.c
rename to contrib/egg/egg-pill-box.c
index fdf1e56..a40630a 100644
--- a/src/greeter/gb-greeter-pill-box.c
+++ b/contrib/egg/egg-pill-box.c
@@ -1,4 +1,4 @@
-/* gb-greeter-pill-box.c
+/* egg-pill-box.c
*
* Copyright (C) 2015 Christian Hergert <christian hergert me>
*
@@ -16,20 +16,20 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#define G_LOG_DOMAIN "gb-greeter-pill-box"
+#define G_LOG_DOMAIN "egg-pill-box"
#include <glib/gi18n.h>
-#include "gb-greeter-pill-box.h"
+#include "egg-pill-box.h"
-struct _GbGreeterPillBox
+struct _EggPillBox
{
GtkEventBox parent_instance;
GtkLabel *label;
};
-G_DEFINE_TYPE (GbGreeterPillBox, gb_greeter_pill_box, GTK_TYPE_EVENT_BOX)
+G_DEFINE_TYPE (EggPillBox, egg_pill_box, GTK_TYPE_EVENT_BOX)
enum {
PROP_0,
@@ -40,42 +40,42 @@ enum {
static GParamSpec *properties [LAST_PROP];
const gchar *
-gb_greeter_pill_box_get_label (GbGreeterPillBox *self)
+egg_pill_box_get_label (EggPillBox *self)
{
- g_return_val_if_fail (GB_IS_GREETER_PILL_BOX (self), NULL);
+ g_return_val_if_fail (EGG_IS_PILL_BOX (self), NULL);
return gtk_label_get_label (self->label);
}
void
-gb_greeter_pill_box_set_label (GbGreeterPillBox *self,
- const gchar *label)
+egg_pill_box_set_label (EggPillBox *self,
+ const gchar *label)
{
- g_return_if_fail (GB_IS_GREETER_PILL_BOX (self));
+ g_return_if_fail (EGG_IS_PILL_BOX (self));
gtk_label_set_label (self->label, label);
}
GtkWidget *
-gb_greeter_pill_box_new (const gchar *label)
+egg_pill_box_new (const gchar *label)
{
- return g_object_new (GB_TYPE_GREETER_PILL_BOX,
+ return g_object_new (EGG_TYPE_PILL_BOX,
"label", label,
NULL);
}
static void
-gb_greeter_pill_box_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
+egg_pill_box_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
{
- GbGreeterPillBox *self = GB_GREETER_PILL_BOX (object);
+ EggPillBox *self = EGG_PILL_BOX (object);
switch (prop_id)
{
case PROP_LABEL:
- g_value_set_string (value, gb_greeter_pill_box_get_label (self));
+ g_value_set_string (value, egg_pill_box_get_label (self));
break;
default:
@@ -84,17 +84,17 @@ gb_greeter_pill_box_get_property (GObject *object,
}
static void
-gb_greeter_pill_box_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
+egg_pill_box_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
{
- GbGreeterPillBox *self = GB_GREETER_PILL_BOX (object);
+ EggPillBox *self = EGG_PILL_BOX (object);
switch (prop_id)
{
case PROP_LABEL:
- gb_greeter_pill_box_set_label (self, g_value_get_string (value));
+ egg_pill_box_set_label (self, g_value_get_string (value));
break;
default:
@@ -103,13 +103,13 @@ gb_greeter_pill_box_set_property (GObject *object,
}
static void
-gb_greeter_pill_box_class_init (GbGreeterPillBoxClass *klass)
+egg_pill_box_class_init (EggPillBoxClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
- object_class->get_property = gb_greeter_pill_box_get_property;
- object_class->set_property = gb_greeter_pill_box_set_property;
+ object_class->get_property = egg_pill_box_get_property;
+ object_class->set_property = egg_pill_box_set_property;
properties [PROP_LABEL] =
g_param_spec_string ("label",
@@ -119,12 +119,12 @@ gb_greeter_pill_box_class_init (GbGreeterPillBoxClass *klass)
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_LABEL, properties [PROP_LABEL]);
- gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/builder/ui/gb-greeter-pill-box.ui");
- gtk_widget_class_bind_template_child (widget_class, GbGreeterPillBox, label);
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/libegg-private/egg-pill-box.ui");
+ gtk_widget_class_bind_template_child (widget_class, EggPillBox, label);
}
static void
-gb_greeter_pill_box_init (GbGreeterPillBox *self)
+egg_pill_box_init (EggPillBox *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
}
diff --git a/src/greeter/gb-greeter-pill-box.h b/contrib/egg/egg-pill-box.h
similarity index 58%
copy from src/greeter/gb-greeter-pill-box.h
copy to contrib/egg/egg-pill-box.h
index 394a333..88faf6c 100644
--- a/src/greeter/gb-greeter-pill-box.h
+++ b/contrib/egg/egg-pill-box.h
@@ -1,4 +1,4 @@
-/* gb-greeter-pill-box.h
+/* egg-pill-box.h
*
* Copyright (C) 2015 Christian Hergert <christian hergert me>
*
@@ -16,22 +16,22 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef GB_GREETER_PILL_BOX_H
-#define GB_GREETER_PILL_BOX_H
+#ifndef EGG_PILL_BOX_H
+#define EGG_PILL_BOX_H
#include <gtk/gtk.h>
G_BEGIN_DECLS
-#define GB_TYPE_GREETER_PILL_BOX (gb_greeter_pill_box_get_type())
+#define EGG_TYPE_PILL_BOX (egg_pill_box_get_type())
-G_DECLARE_FINAL_TYPE (GbGreeterPillBox, gb_greeter_pill_box, GB, GREETER_PILL_BOX, GtkEventBox)
+G_DECLARE_FINAL_TYPE (EggPillBox, egg_pill_box, EGG, PILL_BOX, GtkEventBox)
-GtkWidget *gb_greeter_pill_box_new (const gchar *label);
-const gchar *gb_greeter_pill_box_get_label (GbGreeterPillBox *self);
-void gb_greeter_pill_box_set_label (GbGreeterPillBox *self,
- const gchar *label);
+GtkWidget *egg_pill_box_new (const gchar *label);
+const gchar *egg_pill_box_get_label (EggPillBox *self);
+void egg_pill_box_set_label (EggPillBox *self,
+ const gchar *label);
G_END_DECLS
-#endif /* GB_GREETER_PILL_BOX_H */
+#endif /* EGG_PILL_BOX_H */
diff --git a/data/ui/gb-greeter-pill-box.ui b/contrib/egg/egg-pill-box.ui
similarity index 91%
rename from data/ui/gb-greeter-pill-box.ui
rename to contrib/egg/egg-pill-box.ui
index 069e75f..1fb94f7 100644
--- a/data/ui/gb-greeter-pill-box.ui
+++ b/contrib/egg/egg-pill-box.ui
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
- <!-- interface-requires gtk+ 3.15 -->
- <template class="GbGreeterPillBox" parent="GtkEventBox">
+ <!-- interface-requires gtk+ 3.18 -->
+ <template class="EggPillBox" parent="GtkEventBox">
<style>
<class name="pill-box"/>
</style>
diff --git a/contrib/egg/egg-scrolled-window.c b/contrib/egg/egg-scrolled-window.c
new file mode 100644
index 0000000..5d1ed66
--- /dev/null
+++ b/contrib/egg/egg-scrolled-window.c
@@ -0,0 +1,280 @@
+/* egg-scrolled-window.c
+ *
+ * Copyright (C) 2014 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/>.
+ */
+
+#include <glib/gi18n.h>
+
+#include "egg-scrolled-window.h"
+
+typedef struct
+{
+ gint max_content_height;
+ gint max_content_width;
+} EggScrolledWindowPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (EggScrolledWindow, egg_scrolled_window, GTK_TYPE_SCROLLED_WINDOW)
+
+enum {
+ PROP_0,
+ PROP_MAX_CONTENT_HEIGHT,
+ PROP_MAX_CONTENT_WIDTH,
+ LAST_PROP
+};
+
+static GParamSpec *properties [LAST_PROP];
+
+GtkWidget *
+egg_scrolled_window_new (void)
+{
+ return g_object_new (EGG_TYPE_SCROLLED_WINDOW, NULL);
+}
+
+gint
+egg_scrolled_window_get_max_content_height (EggScrolledWindow *self)
+{
+ EggScrolledWindowPrivate *priv = egg_scrolled_window_get_instance_private (self);
+
+ g_return_val_if_fail (EGG_IS_SCROLLED_WINDOW (self), -1);
+
+ return priv->max_content_height;
+}
+
+/**
+ * egg_scrolled_window_set_max_content_height:
+ * @max_content_height: the max allowed height request or -1 to ignore.
+ *
+ * This function will set the "max-content-height" property. This property is
+ * used to determine the maximum height that the scrolled window will request.
+ *
+ * This is useful if you want to have a scrolled window grow with the child
+ * allocation, but only up to a certain height.
+ */
+void
+egg_scrolled_window_set_max_content_height (EggScrolledWindow *self,
+ gint max_content_height)
+{
+ EggScrolledWindowPrivate *priv = egg_scrolled_window_get_instance_private (self);
+
+ g_return_if_fail (EGG_IS_SCROLLED_WINDOW (self));
+
+ if (max_content_height != priv->max_content_height)
+ {
+ priv->max_content_height = max_content_height;
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MAX_CONTENT_HEIGHT]);
+ gtk_widget_queue_resize (GTK_WIDGET (self));
+ }
+}
+
+gint
+egg_scrolled_window_get_max_content_width (EggScrolledWindow *self)
+{
+ EggScrolledWindowPrivate *priv = egg_scrolled_window_get_instance_private (self);
+
+ g_return_val_if_fail (EGG_IS_SCROLLED_WINDOW (self), -1);
+
+ return priv->max_content_width;
+}
+
+/**
+ * egg_scrolled_window_set_max_content_width:
+ * @max_content_width: the max allowed width request or -1 to ignore.
+ *
+ * This function will set the "max-content-width" property. This property is
+ * used to determine the maximum width that the scrolled window will request.
+ *
+ * This is useful if you want to have a scrolled window grow with the child
+ * allocation, but only up to a certain width.
+ */
+void
+egg_scrolled_window_set_max_content_width (EggScrolledWindow *self,
+ gint max_content_width)
+{
+ EggScrolledWindowPrivate *priv = egg_scrolled_window_get_instance_private (self);
+
+ g_return_if_fail (EGG_IS_SCROLLED_WINDOW (self));
+
+ if (max_content_width != priv->max_content_width)
+ {
+ priv->max_content_width = max_content_width;
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MAX_CONTENT_HEIGHT]);
+ gtk_widget_queue_resize (GTK_WIDGET (self));
+ }
+}
+
+static void
+egg_scrolled_window_get_preferred_height (GtkWidget *widget,
+ gint *minimum_height,
+ gint *natural_height)
+{
+ EggScrolledWindow *self = (EggScrolledWindow *)widget;
+ EggScrolledWindowPrivate *priv = egg_scrolled_window_get_instance_private (self);
+
+ g_return_if_fail (EGG_IS_SCROLLED_WINDOW (self));
+
+ GTK_WIDGET_CLASS (egg_scrolled_window_parent_class)->get_preferred_height (widget, minimum_height,
natural_height);
+
+ if (natural_height)
+ {
+ if (priv->max_content_height > -1)
+ {
+ GtkWidget *child;
+ GtkStyleContext *style;
+ GtkBorder border;
+ gint child_min_height;
+ gint child_nat_height;
+ gint additional;
+
+ if (!(child = gtk_bin_get_child (GTK_BIN (widget))))
+ return;
+
+ style = gtk_widget_get_style_context (widget);
+ gtk_style_context_get_border (style, gtk_widget_get_state_flags (widget), &border);
+ additional = border.top + border.bottom;
+
+ gtk_widget_get_preferred_height (child, &child_min_height, &child_nat_height);
+
+ if ((child_nat_height > *natural_height) && (priv->max_content_height > *natural_height))
+ *natural_height = MIN (priv->max_content_height, child_nat_height) + additional;
+ }
+ }
+}
+
+static void
+egg_scrolled_window_get_preferred_width (GtkWidget *widget,
+ gint *minimum_width,
+ gint *natural_width)
+{
+ EggScrolledWindow *self = (EggScrolledWindow *)widget;
+ EggScrolledWindowPrivate *priv = egg_scrolled_window_get_instance_private (self);
+
+ g_return_if_fail (EGG_IS_SCROLLED_WINDOW (self));
+
+ GTK_WIDGET_CLASS (egg_scrolled_window_parent_class)->get_preferred_width (widget, minimum_width,
natural_width);
+
+ if (natural_width)
+ {
+ if (priv->max_content_width > -1)
+ {
+ GtkWidget *child;
+ GtkStyleContext *style;
+ GtkBorder border;
+ gint child_min_width;
+ gint child_nat_width;
+ gint additional;
+
+ if (!(child = gtk_bin_get_child (GTK_BIN (widget))))
+ return;
+
+ style = gtk_widget_get_style_context (widget);
+ gtk_style_context_get_border (style, gtk_widget_get_state_flags (widget), &border);
+ additional = border.left = border.right + 1;
+
+ gtk_widget_get_preferred_width (child, &child_min_width, &child_nat_width);
+
+ if ((child_nat_width > *natural_width) && (priv->max_content_width > *natural_width))
+ *natural_width = MIN (priv->max_content_width, child_nat_width) + additional;
+ }
+ }
+}
+
+static void
+egg_scrolled_window_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EggScrolledWindow *self = EGG_SCROLLED_WINDOW (object);
+
+ switch (prop_id)
+ {
+ case PROP_MAX_CONTENT_HEIGHT:
+ g_value_set_int (value, egg_scrolled_window_get_max_content_height (self));
+ break;
+
+ case PROP_MAX_CONTENT_WIDTH:
+ g_value_set_int (value, egg_scrolled_window_get_max_content_width (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+egg_scrolled_window_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EggScrolledWindow *self = EGG_SCROLLED_WINDOW (object);
+
+ switch (prop_id)
+ {
+ case PROP_MAX_CONTENT_HEIGHT:
+ egg_scrolled_window_set_max_content_height (self, g_value_get_int (value));
+ break;
+
+ case PROP_MAX_CONTENT_WIDTH:
+ egg_scrolled_window_set_max_content_width (self, g_value_get_int (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+egg_scrolled_window_class_init (EggScrolledWindowClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->get_property = egg_scrolled_window_get_property;
+ object_class->set_property = egg_scrolled_window_set_property;
+
+ widget_class->get_preferred_width = egg_scrolled_window_get_preferred_width;
+ widget_class->get_preferred_height = egg_scrolled_window_get_preferred_height;
+
+ properties [PROP_MAX_CONTENT_HEIGHT] =
+ g_param_spec_int ("max-content-height",
+ "Max Content Height",
+ "The maximum height request that can be made.",
+ -1,
+ G_MAXINT,
+ -1,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_MAX_CONTENT_WIDTH] =
+ g_param_spec_int ("max-content-width",
+ "Max Content Width",
+ "The maximum width request that can be made.",
+ -1,
+ G_MAXINT,
+ -1,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, LAST_PROP, properties);
+}
+
+static void
+egg_scrolled_window_init (EggScrolledWindow *self)
+{
+ EggScrolledWindowPrivate *priv = egg_scrolled_window_get_instance_private (self);
+
+ priv->max_content_height = -1;
+ priv->max_content_width = -1;
+}
diff --git a/src/greeter/gb-greeter-pill-box.h b/contrib/egg/egg-scrolled-window.h
similarity index 53%
copy from src/greeter/gb-greeter-pill-box.h
copy to contrib/egg/egg-scrolled-window.h
index 394a333..9b66dd9 100644
--- a/src/greeter/gb-greeter-pill-box.h
+++ b/contrib/egg/egg-scrolled-window.h
@@ -1,6 +1,6 @@
-/* gb-greeter-pill-box.h
+/* egg-scrolled-window.h
*
- * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ * Copyright (C) 2014 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
@@ -16,22 +16,24 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef GB_GREETER_PILL_BOX_H
-#define GB_GREETER_PILL_BOX_H
+#ifndef EGG_SCROLLED_WINDOW_H
+#define EGG_SCROLLED_WINDOW_H
#include <gtk/gtk.h>
G_BEGIN_DECLS
-#define GB_TYPE_GREETER_PILL_BOX (gb_greeter_pill_box_get_type())
+#define EGG_TYPE_SCROLLED_WINDOW (egg_scrolled_window_get_type())
-G_DECLARE_FINAL_TYPE (GbGreeterPillBox, gb_greeter_pill_box, GB, GREETER_PILL_BOX, GtkEventBox)
+G_DECLARE_DERIVABLE_TYPE (EggScrolledWindow, egg_scrolled_window, EGG, SCROLLED_WINDOW, GtkScrolledWindow)
-GtkWidget *gb_greeter_pill_box_new (const gchar *label);
-const gchar *gb_greeter_pill_box_get_label (GbGreeterPillBox *self);
-void gb_greeter_pill_box_set_label (GbGreeterPillBox *self,
- const gchar *label);
+struct _EggScrolledWindowClass
+{
+ GtkScrolledWindowClass parent_class;
+};
+
+GtkWidget *egg_scrolled_window_new (void);
G_END_DECLS
-#endif /* GB_GREETER_PILL_BOX_H */
+#endif /* EGG_SCROLLED_WINDOW_H */
diff --git a/contrib/egg/egg.gresource.xml b/contrib/egg/egg.gresource.xml
new file mode 100644
index 0000000..41879df
--- /dev/null
+++ b/contrib/egg/egg.gresource.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/libegg-private">
+ <file>egg-pill-box.ui</file>
+ </gresource>
+</gresources>
diff --git a/data/theme/Adwaita-dark.css b/data/theme/Adwaita-dark.css
index 7ae52c2..35d7e2b 100644
--- a/data/theme/Adwaita-dark.css
+++ b/data/theme/Adwaita-dark.css
@@ -15,6 +15,12 @@ treeview.view.cell:selected {
}
+EggPillBox {
+ background-color: #1e1e1e;
+ border-radius: 3px;
+}
+
+
RgGraph {
background-color: #2e3436;
background-size: 8px 8px;
diff --git a/data/theme/Adwaita.css b/data/theme/Adwaita.css
index 5be481f..32ef4f4 100644
--- a/data/theme/Adwaita.css
+++ b/data/theme/Adwaita.css
@@ -15,7 +15,7 @@ GbWorkbench treeview.view.cell:selected {
}
-GbGreeterPillBox {
+EggPillBox {
background-color: #eeeeec;
border-radius: 3px;
}
diff --git a/data/theme/shared.css b/data/theme/shared.css
index a17facf..e5dd4f8 100644
--- a/data/theme/shared.css
+++ b/data/theme/shared.css
@@ -110,20 +110,20 @@ GbSearchDisplayGroup GtkListBox .list-row {
}
-GbGreeterProjectRow,
+IdeGreeterProjectRow,
GtkListBoxRow.with-header {
border-bottom: 1px solid alpha(@borders, 0.4);
}
-GbGreeterWindow .frame {
+IdeGreeterPerspective .frame {
border-top: 1px solid alpha(@borders, 0.4);
border-left: 1px solid alpha(@borders, 0.4);
border-right: 1px solid alpha(@borders, 0.4);
border-bottom: none;
}
/* remove viewport frame */
-GbGreeterWindow .frame > GbScrolledWindow > .frame,
-GbGreeterWindow > GtkBox > GtkScrolledWindow > .frame {
+IdeGreeterPerspective .frame > EggScrolledWindow > .frame,
+IdeGreeterPerspective > GtkBox > EggScrolledWindow > .frame {
border: none;
}
@@ -164,3 +164,27 @@ GbWorkspacePane .pane-separator {
GbEditorFrame textview {
padding-bottom: 250px;
}
+
+
+/* Workbench perspective selection bar */
+/*
+IdeWorkbench > GtkStack > GtkBox.horizontal > GtkStackSwitcher > button {
+ background-image: none;
+ background-color: transparent;
+ border: none;
+ padding: 10px;
+ box-shadow: none;
+ border-radius: 0px;
+}
+IdeWorkbench > GtkStack > GtkBox.horizontal > GtkStackSwitcher > button:active:hover,
+IdeWorkbench > GtkStack > GtkBox.horizontal > GtkStackSwitcher > button:active:checked,
+IdeWorkbench > GtkStack > GtkBox.horizontal > GtkStackSwitcher > button:active {
+ background-color: mix(@theme_bg_color, @borders, 0.75);
+}
+IdeWorkbench > GtkStack > GtkBox.horizontal > GtkStackSwitcher > button:checked {
+ background-color: mix(@theme_bg_color, @borders, 0.5);
+}
+IdeWorkbench > GtkStack > GtkBox.horizontal > GtkStackSwitcher > button:hover {
+ background-color: mix(@theme_bg_color, @borders, 0.25);
+}
+*/
diff --git a/data/ui/ide-editor-perspective.ui b/data/ui/ide-editor-perspective.ui
new file mode 100644
index 0000000..2a58133
--- /dev/null
+++ b/data/ui/ide-editor-perspective.ui
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.18 -->
+ <template class="IdeEditorPerspective" parent="GtkBin">
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">true</property>
+ <property name="label">Editor Perspective</property>
+ </object>
+ </child>
+ </template>
+ <object class="IdeWorkbenchHeaderBar" id="titlebar">
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">horizontal</property>
+ <property name="visible">true</property>
+ <style>
+ <class name="linked"/>
+ </style>
+ <child>
+ <object class="GtkToggleButton">
+ <property name="focus-on-click">false</property>
+ <property name="visible">true</property>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="icon-name">builder-view-left-pane-symbolic</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkToggleButton">
+ <property name="focus-on-click">false</property>
+ <property name="visible">true</property>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="icon-name">builder-view-bottom-pane-symbolic</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkToggleButton">
+ <property name="focus-on-click">false</property>
+ <property name="visible">true</property>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="icon-name">builder-view-right-pane-symbolic</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ <property name="pack-type">end</property>
+ </packing>
+ </child>
+ </object>
+</interface>
diff --git a/data/ui/ide-greeter-perspective.ui b/data/ui/ide-greeter-perspective.ui
new file mode 100644
index 0000000..9e6da6c
--- /dev/null
+++ b/data/ui/ide-greeter-perspective.ui
@@ -0,0 +1,281 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.18 -->
+ <template class="IdeGreeterPerspective" parent="GtkBin">
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <property name="expand">true</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="EggSearchBar">
+ <property name="visible">true</property>
+ <property name="vexpand">false</property>
+ <property name="search-mode-enabled" bind-source="search_button" bind-property="active"
bind-flags="bidirectional"/>
+ <child internal-child="entry">
+ <object class="GtkSearchEntry" id="search_entry">
+ <property name="placeholder-text"></property>
+ <property name="width-chars">45</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolled_window">
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkViewport" id="viewport">
+ <property name="expand">true</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkBox">
+ <property name="halign">center</property>
+ <property name="margin-bottom">32</property>
+ <property name="margin-top">32</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">32</property>
+ <property name="visible">true</property>
+ <property name="width-request">600</property>
+ <child>
+ <object class="GtkBox" id="my_projects_container">
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkLabel" id="my_projects_label">
+ <property name="label" translatable="yes">Recent Projects</property>
+ <property name="visible">true</property>
+ <property name="xalign">0.0</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ <child>
+ <object class="EggScrolledWindow">
+ <property name="min-content-height">100</property>
+ <property name="min-content-width">475</property>
+ <property name="max-content-width">600</property>
+ <property name="visible">true</property>
+ <property name="vscrollbar-policy">never</property>
+ <child>
+ <object class="GtkListBox" id="my_projects_list_box">
+ <property name="visible">true</property>
+ <property name="selection-mode">none</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="other_projects_container">
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkLabel" id="other_projects_label">
+ <property name="label" translatable="yes">Other Projects</property>
+ <property name="visible">true</property>
+ <property name="xalign">0.0</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ <child>
+ <object class="EggScrolledWindow">
+ <property name="min-content-height">100</property>
+ <property name="min-content-width">300</property>
+ <property name="max-content-width">600</property>
+ <property name="visible">true</property>
+ <property name="vscrollbar-policy">never</property>
+ <child>
+ <object class="GtkListBox" id="other_projects_list_box">
+ <property name="visible">true</property>
+ <property name="selection-mode">none</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">true</property>
+ <property name="fill">true</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkActionBar" id="action_bar">
+ <property name="visible">false</property>
+ <child>
+ <object class="GtkButton" id="remove_button">
+ <property name="action-name">perspective.delete-selected-rows</property>
+ <property name="label" translatable="yes">_Remove</property>
+ <property name="use-underline">true</property>
+ <property name="visible">true</property>
+ <property name="sensitive">false</property>
+ <style>
+ <class name="destructive-action"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+ <object class="GtkHeaderBar" id="titlebar">
+ <property name="visible">true</property>
+ <child type="title">
+ <object class="GtkStack" id="title_stack">
+ <property name="visible">true</property>
+ <property name="transition-type">crossfade</property>
+ <child>
+ <object class="GtkLabel" id="browse_title">
+ <property name="visible">true</property>
+ <property name="label" translatable="yes">Select a Project</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="selection_title">
+ <property name="visible">true</property>
+ <property name="label" translatable="yes">Click an item to select</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="new_button">
+ <property name="action-name">app.new-project</property>
+ <property name="visible">true</property>
+ <property name="label" translatable="yes">_New</property>
+ <property name="use-underline">true</property>
+ <style>
+ <class name="text-button"/>
+ </style>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ <property name="pack-type">start</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="search_button">
+ <property name="visible">true</property>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="icon-name">edit-find-symbolic</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ <property name="pack-type">end</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="selection_button">
+ <property name="action-name">perspective.state</property>
+ <property name="action-target">'selection'</property>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="icon-name">object-select-symbolic</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ <property name="pack-type">end</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel_button">
+ <property name="label" translatable="yes">_Cancel</property>
+ <property name="use-underline">true</property>
+ <property name="action-name">perspective.state</property>
+ <property name="action-target">'browse'</property>
+ <style>
+ <class name="text-button"/>
+ </style>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ <property name="pack-type">end</property>
+ </packing>
+ </child>
+ </object>
+ <object class="EggStateMachine" id="state_machine">
+ <property name="state">browse</property>
+ <states>
+ <state name="browse">
+ <object id="titlebar">
+ <property name="show-close-button">true</property>
+ </object>
+ <object id="title_stack">
+ <property name="visible-child">browse_title</property>
+ </object>
+ <object id="cancel_button">
+ <property name="visible">false</property>
+ </object>
+ <object id="selection_button">
+ <property name="visible">true</property>
+ </object>
+ <object id="new_button">
+ <property name="visible">true</property>
+ </object>
+ <object id="action_bar">
+ <property name="visible">false</property>
+ </object>
+ <object id="other_projects_container">
+ <property name="visible">true</property>
+ </object>
+ </state>
+ <state name="selection">
+ <object id="titlebar">
+ <property name="show-close-button">false</property>
+ <style>
+ <class name="selection-mode"/>
+ </style>
+ </object>
+ <object id="action_bar">
+ <property name="visible">true</property>
+ </object>
+ <object id="title_stack">
+ <property name="visible-child">selection_title</property>
+ </object>
+ <object id="cancel_button">
+ <property name="visible">true</property>
+ </object>
+ <object id="selection_button">
+ <property name="visible">false</property>
+ </object>
+ <object id="new_button">
+ <property name="visible">false</property>
+ </object>
+ <object id="other_projects_container">
+ <property name="visible">false</property>
+ </object>
+ </state>
+ </states>
+ </object>
+</interface>
diff --git a/data/ui/ide-greeter-project-row.ui b/data/ui/ide-greeter-project-row.ui
new file mode 100644
index 0000000..b177245
--- /dev/null
+++ b/data/ui/ide-greeter-project-row.ui
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.18 -->
+ <template class="IdeGreeterProjectRow" parent="GtkListBoxRow">
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">horizontal</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkCheckButton" id="checkbox">
+ <property name="margin-start">12</property>
+ <property name="valign">center</property>
+ <property name="vexpand">true</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <property name="margin">12</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">horizontal</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkLabel" id="title_label">
+ <property name="visible">true</property>
+ <property name="hexpand">true</property>
+ <property name="valign">baseline</property>
+ <property name="xalign">0.0</property>
+ <attributes>
+ <attribute name="scale" value="1.2"/>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="date_label">
+ <property name="visible">true</property>
+ <property name="hexpand">true</property>
+ <property name="valign">baseline</property>
+ <property name="xalign">1.0</property>
+ <attributes>
+ <attribute name="scale" value="0.833333"/>
+ </attributes>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="description_label">
+ <property name="single-line-mode">true</property>
+ <property name="ellipsize">end</property>
+ <property name="valign">baseline</property>
+ <property name="visible">true</property>
+ <property name="xalign">0.0</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">horizontal</property>
+ <property name="visible">true</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="location_label">
+ <property name="hexpand">true</property>
+ <property name="visible">true</property>
+ <property name="valign">baseline</property>
+ <property name="xalign">0.0</property>
+ <property name="ellipsize">middle</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <attributes>
+ <attribute name="scale" value="0.833333"/>
+ </attributes>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="languages_box">
+ <property name="spacing">3</property>
+ <property name="orientation">horizontal</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/data/ui/ide-workbench-header-bar.ui b/data/ui/ide-workbench-header-bar.ui
new file mode 100644
index 0000000..39e92b6
--- /dev/null
+++ b/data/ui/ide-workbench-header-bar.ui
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.18 -->
+ <template class="IdeWorkbenchHeaderBar" parent="GtkHeaderBar">
+ <property name="show-close-button">true</property>
+ <child type="title">
+ <object class="GtkSearchEntry" id="search_entry">
+ <property name="hexpand">true</property>
+ <property name="margin-end">6</property>
+ <property name="margin-start">6</property>
+ <property name="max-width-chars">50</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuButton" id="menu_button">
+ <property name="focus-on-click">false</property>
+ <property name="visible">true</property>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="icon-name">open-menu-symbolic</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="pack-type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </template>
+</interface>
diff --git a/data/ui/ide-workbench.ui b/data/ui/ide-workbench.ui
new file mode 100644
index 0000000..185bfb2
--- /dev/null
+++ b/data/ui/ide-workbench.ui
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.17 -->
+ <template class="IdeWorkbench" parent="GtkApplicationWindow">
+ <child type="titlebar">
+ <object class="GtkStack" id="titlebar_stack">
+ <property name="visible">true</property>
+ <property name="transition-type">slide-left-right</property>
+ <property name="transition-duration">333</property>
+ </object>
+ </child>
+ <child>
+ <!--
+ top_stack is used so that we can have a perspective,
+ (the greeter perspective) as the initial perspective without
+ a stack switcher. Once we animate in the real perspectives, we
+ want the switcher. also, we want to animate in the switcher
+ as part of that transition, otherwise it looks clunky.
+ -->
+ <object class="GtkStack" id="top_stack">
+ <property name="visible">true</property>
+ <property name="transition-duration">333</property>
+ <property name="transition-type">slide-left-right</property>
+ <child>
+ <object class="GtkBox">
+ <property name="expand">true</property>
+ <property name="orientation">horizontal</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkStackSwitcher" id="perspectives_stack_switcher">
+ <property name="margin">6</property>
+ <property name="orientation">vertical</property>
+ <property name="stack">perspectives_stack</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkStack" id="perspectives_stack">
+ <property name="homogeneous">false</property>
+ <property name="transition-type">crossfade</property>
+ <property name="transition-duration">333</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="name">perspectives</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/libide/Makefile.am b/libide/Makefile.am
index 881f5ad..e7ab03b 100644
--- a/libide/Makefile.am
+++ b/libide/Makefile.am
@@ -13,10 +13,16 @@ libide_1_0_la_public_sources = \
doap/ide-doap-person.h \
doap/ide-doap.c \
doap/ide-doap.h \
+ editor/ide-editor-perspective.c \
+ editor/ide-editor-perspective.h \
git/ide-git-remote-callbacks.c \
git/ide-git-remote-callbacks.h \
git/ide-git-vcs.c \
git/ide-git-vcs.h \
+ ide-application.c \
+ ide-application.h \
+ ide-application-addin.c \
+ ide-application-addin.h \
ide-back-forward-item.c \
ide-back-forward-item.h \
ide-back-forward-list.c \
@@ -86,9 +92,17 @@ libide_1_0_la_public_sources = \
ide-indent-style.h \
ide-indenter.c \
ide-indenter.h \
+ ide-layout-manager.c \
+ ide-layout-manager.h \
ide-log.c \
ide-log.h \
ide-macros.h \
+ ide-perspective.c \
+ ide-perspective.h \
+ ide-preferences.c \
+ ide-preferences.h \
+ ide-preferences-addin.c \
+ ide-preferences-addin.h \
ide-object.c \
ide-object.h \
ide-pattern-spec.c \
@@ -172,10 +186,18 @@ libide_1_0_la_public_sources = \
ide-unsaved-file.h \
ide-unsaved-files.c \
ide-unsaved-files.h \
+ ide-view.c \
+ ide-view.h \
ide-vcs-uri.c \
ide-vcs-uri.h \
ide-vcs.c \
ide-vcs.h \
+ ide-workbench.c \
+ ide-workbench.h \
+ ide-workbench-addin.c \
+ ide-workbench-addin.h \
+ ide-workbench-header-bar.c \
+ ide-workbench-header-bar.h \
ide-worker.c \
ide-worker.h \
ide.c \
@@ -193,14 +215,23 @@ libide_1_0_la_SOURCES = \
gconstructor.h \
git/ide-git-buffer-change-monitor.c \
git/ide-git-buffer-change-monitor.h \
+ greeter/ide-greeter-perspective.c \
+ greeter/ide-greeter-perspective.h \
+ greeter/ide-greeter-project-row.c \
+ greeter/ide-greeter-project-row.h \
gsettings/ide-gsettings-file-settings.c \
gsettings/ide-gsettings-file-settings.h \
gsettings/ide-language-defaults.c \
gsettings/ide-language-defaults.h \
+ ide-application-actions.c \
+ ide-application-actions.h \
+ ide-application-private.h \
ide-async-helper.c \
ide-async-helper.h \
ide-battery-monitor.c \
ide-battery-monitor.h \
+ ide-css-provider.c \
+ ide-css-provider.h \
ide-debug.h \
ide-extension-util.c \
ide-extension-util.h \
@@ -209,6 +240,8 @@ libide_1_0_la_SOURCES = \
ide-line-change-gutter-renderer.h \
ide-line-diagnostics-gutter-renderer.c \
ide-line-diagnostics-gutter-renderer.h \
+ ide-keybindings.c \
+ ide-keybindings.h \
ide-ref-ptr.c \
ide-ref-ptr.h \
ide-search-reducer.c \
@@ -228,6 +261,7 @@ libide_1_0_la_SOURCES = \
ide-source-view-movements.h \
ide-vim-iter.c \
ide-vim-iter.h \
+ ide-workbench-private.h \
ide-worker-manager.c \
ide-worker-manager.h \
ide-worker-process.c \
@@ -253,11 +287,16 @@ libide_1_0_la_SOURCES = \
util/ide-rgba.h \
util/ide-text-util.c \
util/ide-text-util.h \
+ util/ide-window-settings.c \
+ util/ide-window-settings.h \
$(NULL)
libide_1_0_la_includes = \
$(DEBUG_CFLAGS) \
- -DLIBDIR="\"$(libdir)\"" \
+ -DPACKAGE_DATADIR="\"${datadir}\"" \
+ -DPACKAGE_LOCALE_DIR=\""${datadir}/locale"\" \
+ -DPACKAGE_LIBDIR=\""${libdir}"\" \
+ -DBUILDDIR=\""${abs_top_builddir}"\" \
-I$(top_builddir)/libide \
-I$(top_srcdir)/contrib/egg \
-I$(top_srcdir)/contrib/libeditorconfig \
@@ -266,8 +305,10 @@ libide_1_0_la_includes = \
-I$(srcdir) \
-I$(srcdir)/directory \
-I$(srcdir)/doap \
+ -I$(srcdir)/editor \
-I$(srcdir)/editorconfig \
-I$(srcdir)/git \
+ -I$(srcdir)/greeter \
-I$(srcdir)/gsettings \
-I$(srcdir)/local \
-I$(srcdir)/modelines \
diff --git a/libide/editor/ide-editor-perspective.c b/libide/editor/ide-editor-perspective.c
new file mode 100644
index 0000000..adc7299
--- /dev/null
+++ b/libide/editor/ide-editor-perspective.c
@@ -0,0 +1,121 @@
+/* ide-editor-perspective.c
+ *
+ * Copyright (C) 2015 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-editor-perspective"
+
+#include <glib/gi18n.h>
+
+#include "ide-editor-perspective.h"
+#include "ide-workbench-header-bar.h"
+
+struct _IdeEditorPerspective
+{
+ GtkBin parent_instance;
+
+ IdeWorkbenchHeaderBar *titlebar;
+};
+
+static void ide_perspective_iface_init (IdePerspectiveInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (IdeEditorPerspective, ide_editor_perspective, GTK_TYPE_BIN, 0,
+ G_IMPLEMENT_INTERFACE (IDE_TYPE_PERSPECTIVE, ide_perspective_iface_init))
+
+enum {
+ PROP_0,
+ LAST_PROP
+};
+
+static GParamSpec *properties [LAST_PROP];
+
+static void
+ide_editor_perspective_finalize (GObject *object)
+{
+ G_OBJECT_CLASS (ide_editor_perspective_parent_class)->finalize (object);
+}
+
+static void
+ide_editor_perspective_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id)
+ {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_editor_perspective_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id)
+ {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_editor_perspective_class_init (IdeEditorPerspectiveClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = ide_editor_perspective_finalize;
+ object_class->get_property = ide_editor_perspective_get_property;
+ object_class->set_property = ide_editor_perspective_set_property;
+
+ gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/builder/ui/ide-editor-perspective.ui");
+ gtk_widget_class_bind_template_child (widget_class, IdeEditorPerspective, titlebar);
+}
+
+static void
+ide_editor_perspective_init (IdeEditorPerspective *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+}
+
+static const gchar *
+ide_editor_perspective_get_title (IdePerspective *perspective)
+{
+ return _("Editor");
+}
+
+static GtkWidget *
+ide_editor_perspective_get_titlebar (IdePerspective *perspective)
+{
+ return GTK_WIDGET (IDE_EDITOR_PERSPECTIVE (perspective)->titlebar);
+}
+
+static const gchar *
+ide_editor_perspective_get_icon_name (IdePerspective *perspective)
+{
+ return "text-editor-symbolic";
+}
+
+static void
+ide_perspective_iface_init (IdePerspectiveInterface *iface)
+{
+ iface->get_title = ide_editor_perspective_get_title;
+ iface->get_titlebar = ide_editor_perspective_get_titlebar;
+ iface->get_icon_name = ide_editor_perspective_get_icon_name;
+}
diff --git a/src/greeter/gb-greeter-pill-box.h b/libide/editor/ide-editor-perspective.h
similarity index 53%
copy from src/greeter/gb-greeter-pill-box.h
copy to libide/editor/ide-editor-perspective.h
index 394a333..b8548bc 100644
--- a/src/greeter/gb-greeter-pill-box.h
+++ b/libide/editor/ide-editor-perspective.h
@@ -1,6 +1,6 @@
-/* gb-greeter-pill-box.h
+/* ide-editor-perspective.h
*
- * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ * Copyright (C) 2015 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
@@ -16,22 +16,19 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef GB_GREETER_PILL_BOX_H
-#define GB_GREETER_PILL_BOX_H
+#ifndef IDE_EDITOR_PERSPECTIVE_H
+#define IDE_EDITOR_PERSPECTIVE_H
#include <gtk/gtk.h>
-G_BEGIN_DECLS
+#include "ide-perspective.h"
-#define GB_TYPE_GREETER_PILL_BOX (gb_greeter_pill_box_get_type())
+G_BEGIN_DECLS
-G_DECLARE_FINAL_TYPE (GbGreeterPillBox, gb_greeter_pill_box, GB, GREETER_PILL_BOX, GtkEventBox)
+#define IDE_TYPE_EDITOR_PERSPECTIVE (ide_editor_perspective_get_type())
-GtkWidget *gb_greeter_pill_box_new (const gchar *label);
-const gchar *gb_greeter_pill_box_get_label (GbGreeterPillBox *self);
-void gb_greeter_pill_box_set_label (GbGreeterPillBox *self,
- const gchar *label);
+G_DECLARE_FINAL_TYPE (IdeEditorPerspective, ide_editor_perspective, IDE, EDITOR_PERSPECTIVE, GtkBin)
G_END_DECLS
-#endif /* GB_GREETER_PILL_BOX_H */
+#endif /* IDE_EDITOR_PERSPECTIVE_H */
diff --git a/libide/greeter/ide-greeter-perspective.c b/libide/greeter/ide-greeter-perspective.c
new file mode 100644
index 0000000..f37a4ea
--- /dev/null
+++ b/libide/greeter/ide-greeter-perspective.c
@@ -0,0 +1,699 @@
+/* ide-greeter-perspective.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-greeter-perspective"
+
+#include <glib/gi18n.h>
+
+#include "egg-search-bar.h"
+#include "egg-signal-group.h"
+#include "egg-state-machine.h"
+
+#include "ide-application.h"
+#include "ide-greeter-perspective.h"
+#include "ide-greeter-project-row.h"
+#include "ide-macros.h"
+#include "ide-pattern-spec.h"
+#include "ide-perspective.h"
+#include "ide-workbench.h"
+#include "ide-workbench-private.h"
+
+struct _IdeGreeterPerspective
+{
+ GtkBin parent_instance;
+
+ EggSignalGroup *signal_group;
+ IdeRecentProjects *recent_projects;
+ IdePatternSpec *pattern_spec;
+ GActionMap *actions;
+
+ GtkViewport *viewport;
+ GtkWidget *titlebar;
+ GtkBox *my_projects_container;
+ GtkListBox *my_projects_list_box;
+ GtkBox *other_projects_container;
+ GtkListBox *other_projects_list_box;
+ GtkButton *remove_button;
+ GtkSearchEntry *search_entry;
+ EggStateMachine *state_machine;
+ GtkScrolledWindow *scrolled_window;
+
+ gint selected_count;
+};
+
+static void ide_perspective_iface_init (IdePerspectiveInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (IdeGreeterPerspective, ide_greeter_perspective, GTK_TYPE_BIN, 0,
+ G_IMPLEMENT_INTERFACE (IDE_TYPE_PERSPECTIVE,
+ ide_perspective_iface_init))
+
+enum {
+ PROP_0,
+ PROP_RECENT_PROJECTS,
+ LAST_PROP
+};
+
+static GParamSpec *properties [LAST_PROP];
+
+static GtkWidget *
+ide_greeter_perspective_get_titlebar (IdePerspective *perspective)
+{
+ return IDE_GREETER_PERSPECTIVE (perspective)->titlebar;
+}
+
+static GActionGroup *
+ide_greeter_perspective_get_actions (IdePerspective *perspective)
+{
+ return G_ACTION_GROUP (IDE_GREETER_PERSPECTIVE (perspective)->actions);
+}
+
+static void
+ide_perspective_iface_init (IdePerspectiveInterface *iface)
+{
+ iface->get_actions = ide_greeter_perspective_get_actions;
+ iface->get_titlebar = ide_greeter_perspective_get_titlebar;
+}
+
+static void
+ide_greeter_perspective_first_visible_cb (GtkWidget *widget,
+ gpointer user_data)
+{
+ GtkWidget **row = user_data;
+
+ if ((*row == NULL) && gtk_widget_get_child_visible (widget))
+ *row = widget;
+}
+
+static void
+ide_greeter_perspective__search_entry_activate (IdeGreeterPerspective *self,
+ GtkSearchEntry *search_entry)
+{
+ GtkWidget *row = NULL;
+
+ g_assert (IDE_IS_GREETER_PERSPECTIVE (self));
+ g_assert (GTK_IS_SEARCH_ENTRY (search_entry));
+
+ gtk_container_foreach (GTK_CONTAINER (self->my_projects_list_box),
+ ide_greeter_perspective_first_visible_cb,
+ &row);
+ if (row == NULL)
+ gtk_container_foreach (GTK_CONTAINER (self->other_projects_list_box),
+ ide_greeter_perspective_first_visible_cb,
+ &row);
+
+ if (row != NULL)
+ g_signal_emit_by_name (row, "activate");
+}
+
+IdeRecentProjects *
+ide_greeter_perspective_get_recent_projects (IdeGreeterPerspective *self)
+{
+ g_return_val_if_fail (IDE_IS_GREETER_PERSPECTIVE (self), NULL);
+
+ return self->recent_projects;
+}
+
+static void
+ide_greeter_perspective_apply_filter_cb (GtkWidget *widget,
+ gpointer user_data)
+{
+ gboolean *visible = user_data;
+
+ g_assert (IDE_IS_GREETER_PROJECT_ROW (widget));
+
+ if (gtk_widget_get_child_visible (widget))
+ *visible = TRUE;
+}
+
+static void
+ide_greeter_perspective_apply_filter (IdeGreeterPerspective *self,
+ GtkListBox *list_box,
+ GtkWidget *container)
+{
+ gboolean visible = FALSE;
+
+ g_assert (IDE_IS_GREETER_PERSPECTIVE (self));
+ g_assert (GTK_IS_LIST_BOX (list_box));
+ g_assert (GTK_IS_CONTAINER (container));
+
+ gtk_list_box_invalidate_filter (list_box);
+ gtk_container_foreach (GTK_CONTAINER (list_box), ide_greeter_perspective_apply_filter_cb, &visible);
+ gtk_widget_set_visible (GTK_WIDGET (container), visible);
+}
+
+static void
+ide_greeter_perspective_apply_filter_all (IdeGreeterPerspective *self)
+{
+ const gchar *text;
+
+ g_assert (IDE_IS_GREETER_PERSPECTIVE (self));
+
+ g_clear_pointer (&self->pattern_spec, ide_pattern_spec_unref);
+ if ((text = gtk_entry_get_text (GTK_ENTRY (self->search_entry))))
+ self->pattern_spec = ide_pattern_spec_new (text);
+
+ ide_greeter_perspective_apply_filter (self,
+ self->my_projects_list_box,
+ GTK_WIDGET (self->my_projects_container));
+ ide_greeter_perspective_apply_filter (self,
+ self->other_projects_list_box,
+ GTK_WIDGET (self->other_projects_container));
+}
+
+static void
+ide_greeter_perspective__search_entry_changed (IdeGreeterPerspective *self,
+ GtkSearchEntry *search_entry)
+{
+ g_assert (IDE_IS_GREETER_PERSPECTIVE (self));
+ g_assert (GTK_IS_SEARCH_ENTRY (search_entry));
+
+ ide_greeter_perspective_apply_filter_all (self);
+}
+
+static gboolean
+row_focus_in_event (IdeGreeterPerspective *self,
+ GdkEventFocus *focus,
+ IdeGreeterProjectRow *row)
+{
+ GtkAllocation alloc;
+ GtkAllocation row_alloc;
+ gint dest_x;
+ gint dest_y;
+
+ g_assert (IDE_IS_GREETER_PERSPECTIVE (self));
+
+ gtk_widget_get_allocation (GTK_WIDGET (self->viewport), &alloc);
+ gtk_widget_get_allocation (GTK_WIDGET (row), &row_alloc);
+
+ /*
+ * If we are smaller than the visible area, don't do anything for now.
+ * This can happen during creation of the window and resize process.
+ */
+ if (row_alloc.height > alloc.height)
+ return GDK_EVENT_PROPAGATE;
+
+ if (gtk_widget_translate_coordinates (GTK_WIDGET (row), GTK_WIDGET (self->viewport), 0, 0, &dest_x,
&dest_y))
+ {
+ gint distance = 0;
+
+ if (dest_y < 0)
+ {
+ distance = dest_y;
+ }
+ else if ((dest_y + row_alloc.height) > alloc.height)
+ {
+ distance = dest_y + row_alloc.height - alloc.height;
+ }
+
+ if (distance != 0)
+ {
+ GtkAdjustment *vadj;
+ gdouble value;
+
+ vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (self->viewport));
+ value = gtk_adjustment_get_value (vadj);
+ gtk_adjustment_set_value (vadj, value + distance);
+ }
+ }
+
+ return GDK_EVENT_PROPAGATE;
+}
+
+static gboolean
+selection_to_true (GBinding *binding,
+ const GValue *from_value,
+ GValue *to_value,
+ gpointer user_data)
+{
+ if (G_VALUE_HOLDS_STRING (from_value) && G_VALUE_HOLDS_BOOLEAN (to_value))
+ {
+ const gchar *str;
+
+ str = g_value_get_string (from_value);
+ g_value_set_boolean (to_value, ide_str_equal0 (str, "selection"));
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+ide_greeter_perspective__row_notify_selected (IdeGreeterPerspective *self,
+ GParamSpec *pspec,
+ IdeGreeterProjectRow *row)
+{
+ gboolean selected = FALSE;
+ GAction *action;
+
+ g_assert (IDE_IS_GREETER_PERSPECTIVE (self));
+ g_assert (pspec != NULL);
+ g_assert (IDE_IS_GREETER_PROJECT_ROW (row));
+
+ g_object_get (row, "selected", &selected, NULL);
+ self->selected_count += selected ? 1 : -1;
+
+ action = g_action_map_lookup_action (self->actions, "delete-selected-rows");
+ g_simple_action_set_enabled (G_SIMPLE_ACTION (action), (self->selected_count > 0));
+}
+
+static void
+recent_projects_items_changed (IdeGreeterPerspective *self,
+ guint position,
+ guint removed,
+ guint added,
+ GListModel *list_model)
+{
+ IdeGreeterProjectRow *row;
+ gsize i;
+
+ /*
+ * TODO: We ignore removed out of simplicity for now.
+ * But IdeRecentProjects doesn't currently remove anything anyway.
+ */
+
+ g_assert (IDE_IS_GREETER_PERSPECTIVE (self));
+ g_assert (G_IS_LIST_MODEL (list_model));
+ g_assert (IDE_IS_RECENT_PROJECTS (list_model));
+
+ for (i = 0; i < added; i++)
+ {
+ IdeProjectInfo *project_info;
+ GtkListBox *list_box;
+
+ project_info = g_list_model_get_item (list_model, position + i);
+
+ row = g_object_new (IDE_TYPE_GREETER_PROJECT_ROW,
+ "visible", TRUE,
+ "project-info", project_info,
+ NULL);
+ g_signal_connect_object (row,
+ "focus-in-event",
+ G_CALLBACK (row_focus_in_event),
+ self,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (row,
+ "notify::selected",
+ G_CALLBACK (ide_greeter_perspective__row_notify_selected),
+ self,
+ G_CONNECT_SWAPPED);
+
+ if (ide_project_info_get_is_recent (project_info))
+ {
+ list_box = self->my_projects_list_box;
+ g_object_bind_property_full (self->state_machine, "state",
+ row, "selection-mode",
+ G_BINDING_SYNC_CREATE,
+ selection_to_true, NULL,
+ NULL, NULL);
+ }
+ else
+ {
+ list_box = self->other_projects_list_box;
+ }
+
+ gtk_container_add (GTK_CONTAINER (list_box), GTK_WIDGET (row));
+ }
+
+ ide_greeter_perspective_apply_filter_all (self);
+}
+
+static gint
+ide_greeter_perspective_sort_rows (GtkListBoxRow *row1,
+ GtkListBoxRow *row2,
+ gpointer user_data)
+{
+ IdeProjectInfo *info1;
+ IdeProjectInfo *info2;
+
+ info1 = ide_greeter_project_row_get_project_info (IDE_GREETER_PROJECT_ROW (row1));
+ info2 = ide_greeter_project_row_get_project_info (IDE_GREETER_PROJECT_ROW (row2));
+
+ return ide_project_info_compare (info1, info2);
+}
+
+static void
+ide_greeter_perspective_set_recent_projects (IdeGreeterPerspective *self,
+ IdeRecentProjects *recent_projects)
+{
+ g_return_if_fail (IDE_IS_GREETER_PERSPECTIVE (self));
+ g_return_if_fail (!recent_projects || IDE_IS_RECENT_PROJECTS (recent_projects));
+
+ if (g_set_object (&self->recent_projects, recent_projects))
+ {
+ egg_signal_group_set_target (self->signal_group, recent_projects);
+
+ if (recent_projects != NULL)
+ {
+ GListModel *list_model;
+ guint n_items;
+
+ list_model = G_LIST_MODEL (recent_projects);
+ n_items = g_list_model_get_n_items (list_model);
+ recent_projects_items_changed (self, 0, 0, n_items, list_model);
+ }
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_RECENT_PROJECTS]);
+ }
+}
+
+static gboolean
+ide_greeter_perspective_filter_row (GtkListBoxRow *row,
+ gpointer user_data)
+{
+ IdeGreeterPerspective *self = user_data;
+ IdeGreeterProjectRow *project_row = (IdeGreeterProjectRow *)row;
+ const gchar *search_text;
+ gboolean ret;
+
+ g_assert (IDE_IS_GREETER_PERSPECTIVE (self));
+ g_assert (IDE_IS_GREETER_PROJECT_ROW (project_row));
+
+ if (self->pattern_spec == NULL)
+ return TRUE;
+
+ search_text = ide_greeter_project_row_get_search_text (project_row);
+ ret = ide_pattern_spec_match (self->pattern_spec, search_text);
+
+ return ret;
+}
+
+static void
+ide_greeter_perspective_context_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr(IdeGreeterPerspective) self = user_data;
+ IdeWorkbench *workbench;
+ IdeContext *context;
+ GError *error = NULL;
+
+ g_assert (IDE_IS_GREETER_PERSPECTIVE (self));
+ g_assert (G_IS_ASYNC_RESULT (result));
+
+ context = ide_context_new_finish (result, &error);
+
+ if (context == NULL)
+ {
+ /* TODO: error handling */
+ g_error ("%s", error->message);
+ }
+
+ workbench = IDE_WORKBENCH (gtk_widget_get_toplevel (GTK_WIDGET (self)));
+ _ide_workbench_set_context (workbench, context);
+}
+
+static void
+ide_greeter_perspective__row_activated (IdeGreeterPerspective *self,
+ IdeGreeterProjectRow *row,
+ GtkListBox *list_box)
+{
+ IdeProjectInfo *project_info;
+ GFile *project_file;
+
+ g_assert (IDE_IS_GREETER_PERSPECTIVE (self));
+ g_assert (IDE_IS_GREETER_PROJECT_ROW (row));
+ g_assert (GTK_IS_LIST_BOX (list_box));
+
+ if (ide_str_equal0 (egg_state_machine_get_state (self->state_machine), "selection"))
+ {
+ gboolean selected = FALSE;
+
+ g_object_get (row, "selected", &selected, NULL);
+ g_object_set (row, "selected", !selected, NULL);
+ return;
+ }
+
+ project_info = ide_greeter_project_row_get_project_info (row);
+ project_file = ide_project_info_get_file (project_info);
+
+ gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE);
+ gtk_widget_set_sensitive (GTK_WIDGET (self->titlebar), FALSE);
+
+ /*
+ * TODO: Check if the project is already open somewhere else.
+ */
+
+ ide_context_new_async (project_file,
+ NULL,
+ ide_greeter_perspective_context_cb,
+ g_object_ref (self));
+
+ ide_project_info_set_is_recent (project_info, TRUE);
+}
+
+static gboolean
+ide_greeter_perspective__keynav_failed (IdeGreeterPerspective *self,
+ GtkDirectionType dir,
+ GtkListBox *list_box)
+{
+ g_assert (IDE_IS_GREETER_PERSPECTIVE (self));
+ g_assert (GTK_IS_LIST_BOX (list_box));
+
+ if ((list_box == self->my_projects_list_box) && (dir == GTK_DIR_DOWN))
+ {
+ gtk_widget_child_focus (GTK_WIDGET (self->other_projects_list_box), GTK_DIR_DOWN);
+ return GDK_EVENT_STOP;
+ }
+ else if ((list_box == self->other_projects_list_box) && (dir == GTK_DIR_UP))
+ {
+ gtk_widget_child_focus (GTK_WIDGET (self->my_projects_list_box), GTK_DIR_UP);
+ return GDK_EVENT_STOP;
+ }
+
+ return GDK_EVENT_PROPAGATE;
+}
+
+static void
+delete_selected_rows (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer user_data)
+{
+ IdeGreeterPerspective *self = user_data;
+ GList *rows;
+ GList *iter;
+ GList *projects = NULL;
+
+ g_assert (IDE_IS_GREETER_PERSPECTIVE (self));
+ g_assert (G_IS_SIMPLE_ACTION (action));
+
+ rows = gtk_container_get_children (GTK_CONTAINER (self->my_projects_list_box));
+
+ for (iter = rows; iter; iter = iter->next)
+ {
+ IdeGreeterProjectRow *row = iter->data;
+ gboolean selected = FALSE;
+
+ g_object_get (row, "selected", &selected, NULL);
+
+ if (selected)
+ {
+ IdeProjectInfo *info;
+
+ info = ide_greeter_project_row_get_project_info (row);
+ projects = g_list_prepend (projects, g_object_ref (info));
+ gtk_container_remove (GTK_CONTAINER (self->my_projects_list_box), iter->data);
+ }
+ }
+
+ g_list_free (rows);
+
+ ide_recent_projects_remove (self->recent_projects, projects);
+ g_list_free_full (projects, g_object_unref);
+
+ self->selected_count = 0;
+ g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
+
+ egg_state_machine_set_state (self->state_machine, "browse");
+
+ ide_greeter_perspective_apply_filter_all (self);
+}
+
+static void
+ide_greeter_perspective_constructed (GObject *object)
+{
+ IdeGreeterPerspective *self = (IdeGreeterPerspective *)object;
+ IdeRecentProjects *recent_projects;
+
+ G_OBJECT_CLASS (ide_greeter_perspective_parent_class)->constructed (object);
+
+ recent_projects = ide_application_get_recent_projects (IDE_APPLICATION_DEFAULT);
+ ide_greeter_perspective_set_recent_projects (self, recent_projects);
+}
+
+static void
+ide_greeter_perspective_finalize (GObject *object)
+{
+ IdeGreeterPerspective *self = (IdeGreeterPerspective *)object;
+
+ g_clear_pointer (&self->pattern_spec, ide_pattern_spec_unref);
+ g_clear_object (&self->signal_group);
+ g_clear_object (&self->recent_projects);
+
+ G_OBJECT_CLASS (ide_greeter_perspective_parent_class)->finalize (object);
+}
+
+static void
+ide_greeter_perspective_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdeGreeterPerspective *self = IDE_GREETER_PERSPECTIVE (object);
+
+ switch (prop_id)
+ {
+ case PROP_RECENT_PROJECTS:
+ g_value_set_object (value, ide_greeter_perspective_get_recent_projects (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_greeter_perspective_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdeGreeterPerspective *self = IDE_GREETER_PERSPECTIVE (object);
+
+ switch (prop_id)
+ {
+ case PROP_RECENT_PROJECTS:
+ ide_greeter_perspective_set_recent_projects (self, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_greeter_perspective_class_init (IdeGreeterPerspectiveClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = ide_greeter_perspective_finalize;
+ object_class->constructed = ide_greeter_perspective_constructed;
+ object_class->get_property = ide_greeter_perspective_get_property;
+ object_class->set_property = ide_greeter_perspective_set_property;
+
+ properties [PROP_RECENT_PROJECTS] =
+ g_param_spec_object ("recent-projects",
+ "Recent Projects",
+ "The recent projects that have been mined.",
+ IDE_TYPE_RECENT_PROJECTS,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, LAST_PROP, properties);
+
+ gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/builder/ui/ide-greeter-perspective.ui");
+ gtk_widget_class_bind_template_child (widget_class, IdeGreeterPerspective, titlebar);
+ gtk_widget_class_bind_template_child (widget_class, IdeGreeterPerspective, my_projects_container);
+ gtk_widget_class_bind_template_child (widget_class, IdeGreeterPerspective, my_projects_list_box);
+ gtk_widget_class_bind_template_child (widget_class, IdeGreeterPerspective, other_projects_container);
+ gtk_widget_class_bind_template_child (widget_class, IdeGreeterPerspective, other_projects_list_box);
+ gtk_widget_class_bind_template_child (widget_class, IdeGreeterPerspective, remove_button);
+ gtk_widget_class_bind_template_child (widget_class, IdeGreeterPerspective, search_entry);
+ gtk_widget_class_bind_template_child (widget_class, IdeGreeterPerspective, scrolled_window);
+ gtk_widget_class_bind_template_child (widget_class, IdeGreeterPerspective, state_machine);
+ gtk_widget_class_bind_template_child (widget_class, IdeGreeterPerspective, viewport);
+}
+
+static void
+ide_greeter_perspective_init (IdeGreeterPerspective *self)
+{
+ GActionEntry actions[] = {
+ { "delete-selected-rows", delete_selected_rows },
+ };
+ GAction *action;
+
+ self->signal_group = egg_signal_group_new (IDE_TYPE_RECENT_PROJECTS);
+ egg_signal_group_connect_object (self->signal_group,
+ "items-changed",
+ G_CALLBACK (recent_projects_items_changed),
+ self,
+ G_CONNECT_SWAPPED);
+
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ g_signal_connect_object (self->search_entry,
+ "activate",
+ G_CALLBACK (ide_greeter_perspective__search_entry_activate),
+ self,
+ G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (self->search_entry,
+ "changed",
+ G_CALLBACK (ide_greeter_perspective__search_entry_changed),
+ self,
+ G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (self->my_projects_list_box,
+ "row-activated",
+ G_CALLBACK (ide_greeter_perspective__row_activated),
+ self,
+ G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (self->my_projects_list_box,
+ "keynav-failed",
+ G_CALLBACK (ide_greeter_perspective__keynav_failed),
+ self,
+ G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (self->other_projects_list_box,
+ "row-activated",
+ G_CALLBACK (ide_greeter_perspective__row_activated),
+ self,
+ G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (self->other_projects_list_box,
+ "keynav-failed",
+ G_CALLBACK (ide_greeter_perspective__keynav_failed),
+ self,
+ G_CONNECT_SWAPPED);
+
+ gtk_list_box_set_sort_func (self->my_projects_list_box,
+ ide_greeter_perspective_sort_rows,
+ NULL, NULL);
+ gtk_list_box_set_sort_func (self->other_projects_list_box,
+ ide_greeter_perspective_sort_rows,
+ NULL, NULL);
+
+ gtk_list_box_set_filter_func (self->my_projects_list_box,
+ ide_greeter_perspective_filter_row,
+ self, NULL);
+ gtk_list_box_set_filter_func (self->other_projects_list_box,
+ ide_greeter_perspective_filter_row,
+ self, NULL);
+
+ self->actions = G_ACTION_MAP (g_simple_action_group_new ());
+
+ action = egg_state_machine_create_action (self->state_machine, "state");
+ g_action_map_add_action (self->actions, action);
+ g_object_unref (action);
+
+ g_action_map_add_action_entries (self->actions, actions, G_N_ELEMENTS (actions), self);
+
+ action = g_action_map_lookup_action (self->actions, "delete-selected-rows");
+ g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
+}
diff --git a/src/greeter/gb-greeter-pill-box.h b/libide/greeter/ide-greeter-perspective.h
similarity index 58%
copy from src/greeter/gb-greeter-pill-box.h
copy to libide/greeter/ide-greeter-perspective.h
index 394a333..5512ffe 100644
--- a/src/greeter/gb-greeter-pill-box.h
+++ b/libide/greeter/ide-greeter-perspective.h
@@ -1,4 +1,4 @@
-/* gb-greeter-pill-box.h
+/* ide-greeter-perspective.h
*
* Copyright (C) 2015 Christian Hergert <christian hergert me>
*
@@ -16,22 +16,19 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef GB_GREETER_PILL_BOX_H
-#define GB_GREETER_PILL_BOX_H
+#ifndef IDE_GREETER_PERSPECTIVE_H
+#define IDE_GREETER_PERSPECTIVE_H
#include <gtk/gtk.h>
-G_BEGIN_DECLS
+#include "ide-recent-projects.h"
-#define GB_TYPE_GREETER_PILL_BOX (gb_greeter_pill_box_get_type())
+G_BEGIN_DECLS
-G_DECLARE_FINAL_TYPE (GbGreeterPillBox, gb_greeter_pill_box, GB, GREETER_PILL_BOX, GtkEventBox)
+#define IDE_TYPE_GREETER_PERSPECTIVE (ide_greeter_perspective_get_type())
-GtkWidget *gb_greeter_pill_box_new (const gchar *label);
-const gchar *gb_greeter_pill_box_get_label (GbGreeterPillBox *self);
-void gb_greeter_pill_box_set_label (GbGreeterPillBox *self,
- const gchar *label);
+G_DECLARE_FINAL_TYPE (IdeGreeterPerspective, ide_greeter_perspective, IDE, GREETER_PERSPECTIVE, GtkBin)
G_END_DECLS
-#endif /* GB_GREETER_PILL_BOX_H */
+#endif /* IDE_GREETER_PERSPECTIVE_H */
diff --git a/libide/greeter/ide-greeter-project-row.c b/libide/greeter/ide-greeter-project-row.c
new file mode 100644
index 0000000..6988216
--- /dev/null
+++ b/libide/greeter/ide-greeter-project-row.c
@@ -0,0 +1,350 @@
+/* ide-greeter-project-row.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-greeter-project-row"
+
+#include <glib/gi18n.h>
+
+#include "egg-binding-group.h"
+#include "egg-date-time.h"
+#include "egg-pill-box.h"
+
+#include "ide-greeter-project-row.h"
+
+struct _IdeGreeterProjectRow
+{
+ GtkListBoxRow parent_instance;
+
+ IdeProjectInfo *project_info;
+ EggBindingGroup *bindings;
+ gchar *search_text;
+
+ GtkLabel *date_label;
+ GtkLabel *description_label;
+ GtkBox *languages_box;
+ GtkLabel *location_label;
+ GtkLabel *title_label;
+ GtkCheckButton *checkbox;
+};
+
+G_DEFINE_TYPE (IdeGreeterProjectRow, ide_greeter_project_row, GTK_TYPE_LIST_BOX_ROW)
+
+enum {
+ PROP_0,
+ PROP_PROJECT_INFO,
+ PROP_SELECTED,
+ PROP_SELECTION_MODE,
+ LAST_PROP
+};
+
+static GParamSpec *properties [LAST_PROP];
+static GFile *home_dir;
+
+void
+ide_greeter_project_row_set_selection_mode (IdeGreeterProjectRow *self,
+ gboolean selection_mode)
+{
+ g_return_if_fail (IDE_IS_GREETER_PROJECT_ROW (self));
+
+ gtk_widget_set_visible (GTK_WIDGET (self->checkbox), selection_mode);
+}
+
+IdeProjectInfo *
+ide_greeter_project_row_get_project_info (IdeGreeterProjectRow *self)
+{
+ g_return_val_if_fail (IDE_IS_GREETER_PROJECT_ROW (self), NULL);
+
+ return self->project_info;
+}
+
+static void
+ide_greeter_project_row_create_search_text (IdeGreeterProjectRow *self,
+ IdeProjectInfo *project_info)
+{
+ const gchar *tmp;
+ IdeDoap *doap;
+ GString *str;
+
+ g_assert (IDE_IS_GREETER_PROJECT_ROW (self));
+
+ str = g_string_new (NULL);
+
+ if ((tmp = ide_project_info_get_name (project_info)))
+ {
+ g_autofree gchar *downcase = g_utf8_strdown (g_strdup (tmp), -1);
+
+ g_string_append (str, tmp);
+ g_string_append (str, " ");
+ g_string_append (str, downcase);
+ g_string_append (str, " ");
+ }
+
+ if ((tmp = ide_project_info_get_description (project_info)))
+ {
+ g_string_append (str, tmp);
+ g_string_append (str, " ");
+ }
+
+ doap = ide_project_info_get_doap (project_info);
+
+ if (doap != NULL)
+ {
+ if ((tmp = ide_doap_get_description (doap)))
+ {
+ g_string_append (str, tmp);
+ g_string_append (str, " ");
+ }
+ }
+
+ g_free (self->search_text);
+ self->search_text = g_strdelimit (g_string_free (str, FALSE), "\n", ' ');
+}
+
+static void
+ide_greeter_project_row_add_languages (IdeGreeterProjectRow *self,
+ IdeProjectInfo *project_info)
+{
+ gchar **languages;
+
+ g_return_if_fail (IDE_IS_GREETER_PROJECT_ROW (self));
+ g_return_if_fail (IDE_IS_PROJECT_INFO (project_info));
+
+ if ((languages = ide_project_info_get_languages (project_info)))
+ {
+ guint len = g_strv_length (languages);
+ gsize i;
+
+ for (i = len; i > 0; i--)
+ {
+ const gchar *name = languages [i - 1];
+ GtkWidget *pill;
+
+ pill = g_object_new (EGG_TYPE_PILL_BOX,
+ "visible", TRUE,
+ "label", name,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (self->languages_box), pill);
+ }
+ }
+}
+
+static void
+ide_greeter_project_row_set_project_info (IdeGreeterProjectRow *self,
+ IdeProjectInfo *project_info)
+{
+ g_return_if_fail (IDE_IS_GREETER_PROJECT_ROW (self));
+ g_return_if_fail (!project_info || IDE_IS_PROJECT_INFO (project_info));
+
+ if (g_set_object (&self->project_info, project_info))
+ {
+ egg_binding_group_set_source (self->bindings, project_info);
+
+ if (project_info != NULL)
+ {
+ ide_greeter_project_row_add_languages (self, project_info);
+ ide_greeter_project_row_create_search_text (self, project_info);
+ }
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PROJECT_INFO]);
+ }
+}
+
+static gboolean
+humanize_date_time (GBinding *binding,
+ const GValue *from_value,
+ GValue *to_value,
+ gpointer user_data)
+{
+ GDateTime *dt;
+ gchar *str;
+
+ g_assert (G_VALUE_HOLDS (from_value, G_TYPE_DATE_TIME));
+ g_assert (G_VALUE_HOLDS (to_value, G_TYPE_STRING));
+
+ if (!(dt = g_value_get_boxed (from_value)))
+ return FALSE;
+
+ str = egg_date_time_format_for_display (dt);
+ g_value_take_string (to_value, str);
+
+ return TRUE;
+}
+
+static gboolean
+truncate_location (GBinding *binding,
+ const GValue *from_value,
+ GValue *to_value,
+ gpointer user_data)
+{
+ GFile *file;
+ gchar *uri;
+
+ g_assert (G_VALUE_HOLDS (from_value, G_TYPE_FILE));
+ g_assert (G_VALUE_HOLDS (to_value, G_TYPE_STRING));
+
+ if (!(file = g_value_get_object (from_value)))
+ return FALSE;
+
+ if (g_file_is_native (file))
+ {
+ gchar *relative_path;
+
+ if ((relative_path = g_file_get_relative_path (home_dir, file)) ||
+ (relative_path = g_file_get_path (file)))
+ {
+ g_value_set_string (to_value, relative_path);
+ return TRUE;
+ }
+ }
+
+ uri = g_file_get_uri (file);
+ g_value_set_string (to_value, uri);
+
+ return TRUE;
+}
+
+const gchar *
+ide_greeter_project_row_get_search_text (IdeGreeterProjectRow *self)
+{
+ g_return_val_if_fail (IDE_IS_GREETER_PROJECT_ROW (self), NULL);
+
+ return self->search_text;
+}
+
+static void
+ide_greeter_project_row_finalize (GObject *object)
+{
+ IdeGreeterProjectRow *self = (IdeGreeterProjectRow *)object;
+
+ g_clear_object (&self->project_info);
+ g_clear_object (&self->bindings);
+ g_clear_pointer (&self->search_text, g_free);
+
+ G_OBJECT_CLASS (ide_greeter_project_row_parent_class)->finalize (object);
+}
+
+static void
+ide_greeter_project_row_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdeGreeterProjectRow *self = IDE_GREETER_PROJECT_ROW (object);
+
+ switch (prop_id)
+ {
+ case PROP_PROJECT_INFO:
+ g_value_set_object (value, ide_greeter_project_row_get_project_info (self));
+ break;
+
+ case PROP_SELECTED:
+ g_object_get_property (G_OBJECT (self->checkbox), "active", value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_greeter_project_row_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdeGreeterProjectRow *self = IDE_GREETER_PROJECT_ROW (object);
+
+ switch (prop_id)
+ {
+ case PROP_SELECTED:
+ g_object_set_property (G_OBJECT (self->checkbox), "active", value);
+ break;
+
+ case PROP_SELECTION_MODE:
+ ide_greeter_project_row_set_selection_mode (self, g_value_get_boolean (value));
+ break;
+
+ case PROP_PROJECT_INFO:
+ ide_greeter_project_row_set_project_info (self, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_greeter_project_row_class_init (IdeGreeterProjectRowClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = ide_greeter_project_row_finalize;
+ object_class->get_property = ide_greeter_project_row_get_property;
+ object_class->set_property = ide_greeter_project_row_set_property;
+
+ gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/builder/ui/ide-greeter-project-row.ui");
+ gtk_widget_class_bind_template_child (widget_class, IdeGreeterProjectRow, checkbox);
+ gtk_widget_class_bind_template_child (widget_class, IdeGreeterProjectRow, date_label);
+ gtk_widget_class_bind_template_child (widget_class, IdeGreeterProjectRow, description_label);
+ gtk_widget_class_bind_template_child (widget_class, IdeGreeterProjectRow, location_label);
+ gtk_widget_class_bind_template_child (widget_class, IdeGreeterProjectRow, languages_box);
+ gtk_widget_class_bind_template_child (widget_class, IdeGreeterProjectRow, title_label);
+
+ properties [PROP_SELECTED] =
+ g_param_spec_boolean ("selected",
+ "Selected",
+ "Selected",
+ FALSE,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_SELECTION_MODE] =
+ g_param_spec_boolean ("selection-mode",
+ "Selection Mode",
+ "Selection Mode",
+ FALSE,
+ (G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_PROJECT_INFO] =
+ g_param_spec_object ("project-info",
+ "Project Information",
+ "The project information to render.",
+ IDE_TYPE_PROJECT_INFO,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, LAST_PROP, properties);
+
+ home_dir = g_file_new_for_path (g_get_home_dir ());
+}
+
+static void
+ide_greeter_project_row_init (IdeGreeterProjectRow *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ self->bindings = egg_binding_group_new ();
+
+ egg_binding_group_bind (self->bindings, "name", self->title_label, "label", 0);
+ egg_binding_group_bind_full (self->bindings, "last-modified-at", self->date_label, "label", 0,
+ humanize_date_time, NULL, NULL, NULL);
+ egg_binding_group_bind_full (self->bindings, "directory", self->location_label, "label", 0,
+ truncate_location, NULL, NULL, NULL);
+ egg_binding_group_bind (self->bindings, "description", self->description_label, "label", 0);
+
+ g_object_bind_property (self->checkbox, "active", self, "selected", 0);
+}
diff --git a/src/greeter/gb-greeter-pill-box.h b/libide/greeter/ide-greeter-project-row.h
similarity index 51%
copy from src/greeter/gb-greeter-pill-box.h
copy to libide/greeter/ide-greeter-project-row.h
index 394a333..8394b0b 100644
--- a/src/greeter/gb-greeter-pill-box.h
+++ b/libide/greeter/ide-greeter-project-row.h
@@ -1,4 +1,4 @@
-/* gb-greeter-pill-box.h
+/* ide-greeter-project-row.h
*
* Copyright (C) 2015 Christian Hergert <christian hergert me>
*
@@ -16,22 +16,24 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef GB_GREETER_PILL_BOX_H
-#define GB_GREETER_PILL_BOX_H
+#ifndef IDE_GREETER_PROJECT_ROW_H
+#define IDE_GREETER_PROJECT_ROW_H
#include <gtk/gtk.h>
+#include "ide-project-info.h"
+
G_BEGIN_DECLS
-#define GB_TYPE_GREETER_PILL_BOX (gb_greeter_pill_box_get_type())
+#define IDE_TYPE_GREETER_PROJECT_ROW (ide_greeter_project_row_get_type())
-G_DECLARE_FINAL_TYPE (GbGreeterPillBox, gb_greeter_pill_box, GB, GREETER_PILL_BOX, GtkEventBox)
+G_DECLARE_FINAL_TYPE (IdeGreeterProjectRow, ide_greeter_project_row, IDE, GREETER_PROJECT_ROW, GtkListBoxRow)
-GtkWidget *gb_greeter_pill_box_new (const gchar *label);
-const gchar *gb_greeter_pill_box_get_label (GbGreeterPillBox *self);
-void gb_greeter_pill_box_set_label (GbGreeterPillBox *self,
- const gchar *label);
+IdeProjectInfo *ide_greeter_project_row_get_project_info (IdeGreeterProjectRow *self);
+const gchar *ide_greeter_project_row_get_search_text (IdeGreeterProjectRow *self);
+void ide_greeter_project_row_set_selection_mode (IdeGreeterProjectRow *self,
+ gboolean selection_mode);
G_END_DECLS
-#endif /* GB_GREETER_PILL_BOX_H */
+#endif /* IDE_GREETER_PROJECT_ROW_H */
diff --git a/libide/ide-application-actions.c b/libide/ide-application-actions.c
new file mode 100644
index 0000000..0bd0970
--- /dev/null
+++ b/libide/ide-application-actions.c
@@ -0,0 +1,317 @@
+/* ide-application-actions.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-application-actions"
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include "ide-application.h"
+#include "ide-application-actions.h"
+#include "ide-application-credits.h"
+#include "ide-application-private.h"
+#include "ide-debug.h"
+#include "ide-workbench.h"
+
+static void
+ide_application_actions_preferences (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer user_data)
+{
+#if 0
+ IdeApplication *self = user_data;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_APPLICATION (self));
+
+ if (self->preferences_window == NULL)
+ {
+ IdePreferencesWindow *window;
+
+ window = g_object_new (IDE_TYPE_PREFERENCES_WINDOW,
+ "type-hint", GDK_WINDOW_TYPE_HINT_DIALOG,
+ "window-position", GTK_WIN_POS_CENTER,
+ NULL);
+ ide_set_weak_pointer (&self->preferences_window, window);
+ }
+
+ gtk_window_present (GTK_WINDOW (self->preferences_window));
+
+ IDE_EXIT;
+#endif
+}
+
+#if 0
+static void
+ide_application_actions_support (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer user_data)
+{
+ IdeApplication *self = user_data;
+ GtkWidget *dialog;
+ gchar *text = NULL;
+ GList *windows;
+ GError *error = NULL;
+ gchar *str = NULL;
+ gchar *log_path = NULL;
+ gchar *name = NULL;
+
+ name = g_strdup_printf ("gnome-builder-%u.log", (int)getpid ());
+ log_path = g_build_filename (g_get_home_dir (), name, NULL);
+ g_free (name);
+
+ windows = gtk_application_get_windows (GTK_APPLICATION (self));
+
+ str = ide_get_support_log ();
+
+ if (!g_file_set_contents (log_path, str, -1, &error))
+ {
+ g_printerr ("%s\n", error->message);
+ goto cleanup;
+ }
+
+ text = g_strdup_printf (_("The support log file has been written to '%s'. "
+ "Please provide this file as an attachment on "
+ "your bug report or support request."),
+ log_path);
+
+ g_message ("%s", text);
+
+ dialog = gtk_message_dialog_new (windows ? windows->data : NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_INFO,
+ GTK_BUTTONS_CLOSE,
+ "%s", text);
+ gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+ g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
+ gtk_window_present (GTK_WINDOW (dialog));
+
+cleanup:
+ g_free (text);
+ g_clear_error (&error);
+ g_free (str);
+ g_free (log_path);
+}
+#endif
+
+static void
+ide_application_actions_quit (GSimpleAction *action,
+ GVariant *param,
+ gpointer user_data)
+{
+ IdeApplication *self = user_data;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_APPLICATION (self));
+
+ g_application_quit (G_APPLICATION (self));
+
+ IDE_EXIT;
+}
+
+static void
+ide_application_actions_about (GSimpleAction *action,
+ GVariant *param,
+ gpointer user_data)
+{
+ IdeApplication *self = user_data;
+ GtkDialog *dialog;
+ GtkWindow *parent = NULL;
+ GList *iter;
+ GList *windows;
+
+ g_assert (IDE_IS_APPLICATION (self));
+
+ windows = gtk_application_get_windows (GTK_APPLICATION (self));
+
+ for (iter = windows; iter; iter = iter->next)
+ {
+ if (IDE_IS_WORKBENCH (iter->data))
+ {
+ parent = iter->data;
+ break;
+ }
+ }
+
+ dialog = g_object_new (GTK_TYPE_ABOUT_DIALOG,
+ "artists", ide_application_credits_artists,
+ "authors", ide_application_credits_authors,
+ "comments", _("An IDE for GNOME"),
+ "documenters", ide_application_credits_documenters,
+ "license-type", GTK_LICENSE_GPL_3_0,
+ "logo-icon-name", "builder",
+ "modal", FALSE,
+ "program-name", _("GNOME Builder"),
+ "transient-for", parent,
+ "translator-credits", _("translator-credits"),
+ "version", PACKAGE_VERSION,
+ "website", "https://wiki.gnome.org/Apps/Builder",
+ "website-label", _("Learn more about GNOME Builder"),
+ NULL);
+ gtk_about_dialog_add_credit_section (GTK_ABOUT_DIALOG (dialog),
+ _("Funded By"),
+ ide_application_credits_funders);
+
+ g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
+ gtk_window_present (GTK_WINDOW (dialog));
+}
+
+#if 0
+static void
+ide_application_actions_open_project (GSimpleAction *action,
+ GVariant *variant,
+ gpointer user_data)
+{
+ IdeApplication *self = user_data;
+
+ g_assert (IDE_IS_APPLICATION (self));
+
+ ide_application_show_projects_window (self);
+}
+
+static void
+ide_application_actions_open_project_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeApplication *self = (IdeApplication *)object;
+ g_autoptr(IdeNewProjectDialog) window = user_data;
+ g_autoptr(GError) error = NULL;
+ GtkWindow *transient_for;
+
+ g_assert (IDE_IS_NEW_PROJECT_DIALOG (window));
+
+ if (!ide_application_open_project_finish (self, result, &error))
+ {
+ /* todo: warning message */
+ g_warning ("%s", error->message);
+ }
+
+ transient_for = gtk_window_get_transient_for (GTK_WINDOW (window));
+
+ if (IDE_IS_GREETER_WINDOW (transient_for))
+ g_object_ref (transient_for);
+ else
+ transient_for = NULL;
+
+ gtk_widget_destroy (GTK_WIDGET (window));
+
+ if (transient_for != NULL)
+ {
+ gtk_widget_destroy (GTK_WIDGET (transient_for));
+ g_object_unref (transient_for);
+ }
+}
+
+static void
+ide_application_actions__window_open_project (IdeApplication *self,
+ GFile *project_file,
+ IdeNewProjectDialog *window)
+{
+ g_assert (IDE_IS_APPLICATION (self));
+ g_assert (G_IS_FILE (project_file));
+ g_assert (IDE_IS_NEW_PROJECT_DIALOG (window));
+
+ ide_application_open_project_async (self, project_file, NULL, NULL,
+ ide_application_actions_open_project_cb,
+ g_object_ref (window));
+}
+
+static void
+ide_application_actions_new_project (GSimpleAction *action,
+ GVariant *variant,
+ gpointer user_data)
+{
+ IdeApplication *self = user_data;
+ GtkWindow *transient_for = NULL;
+ GtkWindow *window;
+ GList *windows;
+
+ g_assert (IDE_IS_APPLICATION (self));
+
+ for (windows = gtk_window_group_list_windows (self->greeter_group);
+ windows;
+ windows = windows->next)
+ {
+ if (IDE_IS_NEW_PROJECT_DIALOG (windows->data))
+ {
+ gtk_window_present (windows->data);
+ goto cleanup;
+ }
+ else if (IDE_IS_GREETER_WINDOW (windows->data))
+ {
+ transient_for = windows->data;
+ }
+ }
+
+ window = g_object_new (IDE_TYPE_NEW_PROJECT_DIALOG,
+ "type-hint", GDK_WINDOW_TYPE_HINT_DIALOG,
+ "transient-for", transient_for,
+ "window-position", transient_for ? GTK_WIN_POS_CENTER_ON_PARENT
+ : GTK_WIN_POS_CENTER,
+ NULL);
+
+ g_signal_connect_object (window,
+ "open-project",
+ G_CALLBACK (ide_application_actions__window_open_project),
+ self,
+ G_CONNECT_SWAPPED);
+
+ gtk_window_group_add_window (self->greeter_group, GTK_WINDOW (window));
+ gtk_window_present (window);
+
+cleanup:
+ g_list_free (windows);
+}
+
+static void
+ide_application_actions_shortcuts (GSimpleAction *action,
+ GVariant *variant,
+ gpointer user_data)
+{
+ IdeShortcutsWindow *window;
+
+ window = g_object_new (IDE_TYPE_SHORTCUTS_WINDOW,
+ "window-position", GTK_WIN_POS_CENTER,
+ "default-width", 800,
+ "default-height", 600,
+ NULL);
+
+ gtk_window_present (GTK_WINDOW (window));
+}
+#endif
+
+static const GActionEntry IdeApplicationActions[] = {
+ { "about", ide_application_actions_about },
+ //{ "open-project", ide_application_actions_open_project },
+ //{ "new-project", ide_application_actions_new_project },
+ { "preferences", ide_application_actions_preferences },
+ { "quit", ide_application_actions_quit },
+ //{ "shortcuts", ide_application_actions_shortcuts },
+ //{ "support", ide_application_actions_support },
+};
+
+void
+ide_application_actions_init (IdeApplication *self)
+{
+ g_action_map_add_action_entries (G_ACTION_MAP (self), IdeApplicationActions,
+ G_N_ELEMENTS (IdeApplicationActions), self);
+}
diff --git a/src/greeter/gb-greeter-pill-box.h b/libide/ide-application-actions.h
similarity index 56%
copy from src/greeter/gb-greeter-pill-box.h
copy to libide/ide-application-actions.h
index 394a333..e4cb961 100644
--- a/src/greeter/gb-greeter-pill-box.h
+++ b/libide/ide-application-actions.h
@@ -1,4 +1,4 @@
-/* gb-greeter-pill-box.h
+/* ide-application-actions.h
*
* Copyright (C) 2015 Christian Hergert <christian hergert me>
*
@@ -16,22 +16,15 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef GB_GREETER_PILL_BOX_H
-#define GB_GREETER_PILL_BOX_H
+#ifndef IDE_APPLICATION_ACTIONS_H
+#define IDE_APPLICATION_ACTIONS_H
-#include <gtk/gtk.h>
+#include "ide-application.h"
G_BEGIN_DECLS
-#define GB_TYPE_GREETER_PILL_BOX (gb_greeter_pill_box_get_type())
-
-G_DECLARE_FINAL_TYPE (GbGreeterPillBox, gb_greeter_pill_box, GB, GREETER_PILL_BOX, GtkEventBox)
-
-GtkWidget *gb_greeter_pill_box_new (const gchar *label);
-const gchar *gb_greeter_pill_box_get_label (GbGreeterPillBox *self);
-void gb_greeter_pill_box_set_label (GbGreeterPillBox *self,
- const gchar *label);
+void ide_application_actions_init (IdeApplication *self);
G_END_DECLS
-#endif /* GB_GREETER_PILL_BOX_H */
+#endif /* IDE_APPLICATION_ACTIONS_H */
diff --git a/libide/ide-application-addin.c b/libide/ide-application-addin.c
new file mode 100644
index 0000000..d03d5f5
--- /dev/null
+++ b/libide/ide-application-addin.c
@@ -0,0 +1,81 @@
+/* ide-application-addin.c
+ *
+ * Copyright (C) 2015 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ide-application-addin.h"
+
+G_DEFINE_INTERFACE (IdeApplicationAddin, ide_application_addin, G_TYPE_OBJECT)
+
+static void
+ide_application_addin_real_load (IdeApplicationAddin *self,
+ IdeApplication *application)
+{
+}
+
+static void
+ide_application_addin_real_unload (IdeApplicationAddin *self,
+ IdeApplication *application)
+{
+}
+
+static void
+ide_application_addin_default_init (IdeApplicationAddinInterface *iface)
+{
+ iface->load = ide_application_addin_real_load;
+ iface->unload = ide_application_addin_real_unload;
+}
+
+/**
+ * ide_application_addin_load:
+ * @self: An #IdeApplicationAddin.
+ * @application: An #IdeApplication.
+ *
+ * This interface method is called when the application is started or
+ * the plugin has just been activated.
+ *
+ * Use this to setup code in your plugin that needs to be loaded once
+ * per application process.
+ */
+void
+ide_application_addin_load (IdeApplicationAddin *self,
+ IdeApplication *application)
+{
+ g_return_if_fail (IDE_IS_APPLICATION_ADDIN (self));
+ g_return_if_fail (IDE_IS_APPLICATION (application));
+
+ IDE_APPLICATION_ADDIN_GET_IFACE (self)->load (self, application);
+}
+
+/**
+ * ide_application_addin_unload:
+ * @self: An #IdeApplicationAddin.
+ * @application: An #IdeApplication.
+ *
+ * This inteface method is called when the application is shutting down
+ * or the plugin has been unloaded.
+ *
+ * Use this function to cleanup after anything setup in ide_application_addin_load().
+ */
+void
+ide_application_addin_unload (IdeApplicationAddin *self,
+ IdeApplication *application)
+{
+ g_return_if_fail (IDE_IS_APPLICATION_ADDIN (self));
+ g_return_if_fail (IDE_IS_APPLICATION (application));
+
+ IDE_APPLICATION_ADDIN_GET_IFACE (self)->unload (self, application);
+}
diff --git a/libide/ide-application-addin.h b/libide/ide-application-addin.h
new file mode 100644
index 0000000..8ecde9b
--- /dev/null
+++ b/libide/ide-application-addin.h
@@ -0,0 +1,47 @@
+/* ide-application-addin.h
+ *
+ * Copyright (C) 2015 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/>.
+ */
+
+#ifndef IDE_APPLICATION_ADDIN_H
+#define IDE_APPLICATION_ADDIN_H
+
+#include "ide-application.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_APPLICATION_ADDIN (ide_application_addin_get_type())
+
+G_DECLARE_INTERFACE (IdeApplicationAddin, ide_application_addin, IDE, APPLICATION_ADDIN, GObject)
+
+struct _IdeApplicationAddinInterface
+{
+ GTypeInterface parent_interface;
+
+ void (*load) (IdeApplicationAddin *self,
+ IdeApplication *application);
+ void (*unload) (IdeApplicationAddin *self,
+ IdeApplication *application);
+};
+
+void ide_application_addin_load (IdeApplicationAddin *self,
+ IdeApplication *application);
+void ide_application_addin_unload (IdeApplicationAddin *self,
+ IdeApplication *application);
+
+G_END_DECLS
+
+#endif /* IDE_APPLICATION_ADDIN_H */
diff --git a/libide/ide-application-credits.h b/libide/ide-application-credits.h
new file mode 100644
index 0000000..abe7f92
--- /dev/null
+++ b/libide/ide-application-credits.h
@@ -0,0 +1,554 @@
+/* ide-application-credits.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This file 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
+ * Lesser 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/>.
+ */
+
+#ifndef IDE_APPLICATION_CREDITS_H
+#define IDE_APPLICATION_CREDITS_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+static const gchar *ide_application_credits_artists[] = {
+ "Allan Day",
+ "Hylke Bons",
+ "Jakub Steiner",
+ NULL
+};
+
+static const gchar *ide_application_credits_authors[] = {
+ "Alexander Larsson",
+ "Alexandre Franke",
+ "Andika Triwidada",
+ "Andreas Henriksson",
+ "Antoine Jacoutot",
+ "Aurimas Černius",
+ "Baurzhan Muftakhidinov",
+ "Ben Iofel",
+ "Bernd Homuth",
+ "burningTyger",
+ "Carlos Garnacho",
+ "Carlos Soriano",
+ "Changwoo Ryu",
+ "Chao-Hsiung Liao",
+ "Cheng-Chia Tseng",
+ "Christian Hergert",
+ "Christian Kirbach",
+ "Cosimo Cecchi",
+ "Damien Lespiau",
+ "Daniel Korostil",
+ "Daniel Mustieles",
+ "David King",
+ "Dimitris Zenios",
+ "Dušan Kazik",
+ "Ekaterina Gerasimova",
+ "Elad Alfassa",
+ "Erick Pérez Castellanos",
+ "Fabiano Fidêncio",
+ "Florian Bäuerle",
+ "Florian Müllner",
+ "Fran Dieguez",
+ "Gabor Kelemen",
+ "Gábor Kelemen",
+ "Garrett Regier",
+ "Giovanni Campagna",
+ "Hashem Nasarat",
+ "Hylke Bons",
+ "Ian Hernandez",
+ "Ignacio Casal Quinteiro",
+ "Igor Gnatenko",
+ "Jakub Steiner",
+ "Jasper St. Pierre",
+ "Jonathon Jongsma",
+ "Jordi Mas",
+ "Kalev Lember",
+ "Kjartan Maraas",
+ "Kristjan SCHMIDT",
+ "Lars Uebernickel",
+ "Lionel Landwerlin",
+ "Marek Černocký",
+ "Matej Urbančič",
+ "Mathieu Bridon",
+ "Matthias Clasen",
+ "Megh Parikh",
+ "Michael Catanzaro",
+ "Mohan R",
+ "Muhammet Kara",
+ "Мирослав Николић",
+ "Paolo Borelli",
+ "Patrick Griffis",
+ "Pedro Albuquerque",
+ "Pete Travis",
+ "Piotr Drąg",
+ "Ray Strode",
+ "Roberto Majadas",
+ "Samir Ribic",
+ "Sébastien Lafargue",
+ "Timm Bäder",
+ "TingPing",
+ "Ting-Wei Lan",
+ "Tobias Schönberg",
+ "Tom Tryfonidis",
+ "Trinh Anh Ngoc",
+ "Wolf Vollprecht",
+ "Yannick Inizan",
+ "Yosef Or Boczko",
+ "zilla hmt im",
+ NULL
+};
+
+static const gchar *ide_application_credits_documenters[] = {
+ "Christian Hergert",
+ NULL
+};
+
+static const gchar *ide_application_credits_funders[] = {
+ "曾政嘉",
+ "Aaron Hergert",
+ "Abdul Kadri Gündoğdu",
+ "Abimael Martinez Carrete",
+ "Adam grunden",
+ "Adrian Bradshaw",
+ "Adrian Rocha",
+ "Adrià Arrufat",
+ "Alaska Subedi",
+ "Albert Murciego Rico",
+ "Alessandro Bono",
+ "Alexander B Libby",
+ "Alexander Gleason",
+ "Alexander Khatsayuk",
+ "Alexander Larsson",
+ "Alexander Murray",
+ "Alexander Murray",
+ "Alexandre Amoedo",
+ "Alexandre Franke",
+ "Alexandros Diavatis",
+ "Alfonso de Cala Bravo",
+ "Alfred Santacatalina Gea",
+ "Ambrose Andrews",
+ "Andreas Nilsson",
+ "Andrew Stiegmann",
+ "Andrew Walton",
+ "Anthony Taranto",
+ "Anton Shafarenko",
+ "Aram J Agajanian",
+ "Arne Hoch",
+ "Arturo Buentello G",
+ "Arun Raghavan",
+ "Ashley Sommer",
+ "Aurélien Naldi",
+ "B. Mille-Mathias",
+ "Baldessari Michele",
+ "Bastian Ilsø Hougaard",
+ "Bastien Nocera",
+ "Bastien Nocera",
+ "Benjamin Grimm-Lebsanft",
+ "Bernd Homuth",
+ "Bill Roth",
+ "Brad Taylor",
+ "Brendan Long",
+ "Brijesh Kartha",
+ "Bruce Cowan",
+ "Bruce M Franklin",
+ "Búza Géza",
+ "Canek Pelaez Valdes",
+ "Carlos Soriano Sanchez",
+ "Casey E Megginson",
+ "Cedric Briner",
+ "Cees Meijer",
+ "Centricular Ltd",
+ "Chema Casanova",
+ "Cheng-Chia Tseng",
+ "Chris Bagwell",
+ "Chris Kühl",
+ "Chris Tonkinson",
+ "Christian Hergert",
+ "Christian Lange",
+ "Christopher Brian Sherlock",
+ "Christopher Horton",
+ "Christopher Kim",
+ "Christopher William Bell",
+ "Chun-Sheng Wu",
+ "Cody Russell",
+ "Cosimo Cecchi",
+ "Craig A Cabrey",
+ "Dag Robøle",
+ "Daiki Ueno",
+ "Damián Nohales",
+ "Daniel Buch",
+ "Daniel Dui",
+ "Daniel Espinosa Ortiz",
+ "Daniel Menchaca",
+ "Daniel Nemec",
+ "Daniel Pfeifer",
+ "Daniel Vazquez Rivera",
+ "Danilo Gropelo",
+ "Danylo Korostil",
+ "Debarshi Ray",
+ "Demetris Lambrou",
+ "Dennis Schulmeister",
+ "Denver Gingerich",
+ "Dolgoff ",
+ "Eduardo Silva",
+ "Eitan Isaacson",
+ "Elad Alfassa",
+ "Ellis Kenyo",
+ "Emanuele Aina",
+ "Emanuele Gissi",
+ "Emmanuele Bassi",
+ "Enrique Ocaña González",
+ "Eric Streit",
+ "Eric T Miller",
+ "Erik Helin",
+ "Ernest hershey",
+ "Erwan Bousse",
+ "Erwan Georget",
+ "F. Kooman",
+ "Fabian Alexander Wilms",
+ "Fabien Cortina",
+ "Fabio Valentini",
+ "Faizan Qazi",
+ "Faron S Anslow",
+ "Fasiello Nicomede",
+ "Federico Mena Quintero",
+ "Felix Schröter",
+ "Filipe Santos",
+ "Florian Bäuerle",
+ "Florian Over",
+ "Florian Schweikert",
+ "Florin Florica",
+ "Frank Dietrich",
+ "Frank Hansen",
+ "Fränz Ney",
+ "Fredrik Schaller",
+ "G A Foster",
+ "Gabriel Rauter",
+ "Garrett LeSage",
+ "Georges Seguin",
+ "Georges-Mickael Seguin",
+ "Gianluigi Calcaterra",
+ "Gil Forcada Codinachs",
+ "Giovanni Forte",
+ "Go Min YounG",
+ "Gonzalo Paniagua Javier",
+ "Gordon Martin",
+ "Guilherme Rodrigues",
+ "Guillaume Beaudin",
+ "Guillaume Hain",
+ "Guillaume Quintard",
+ "Gustavo Arejano",
+ "Gustavo N Silva",
+ "Hain Guillaume",
+ "Hannes Ovrén",
+ "Harald Hoyer",
+ "Havoc Pennington",
+ "Hendrik Richter",
+ "Henrique Almeida",
+ "Henry Finucane",
+ "I Martin Rodriguez",
+ "Ian Bolf",
+ "Ian McKellar",
+ "Ignacio Casal Quinteiro",
+ "Igor Gnatenko",
+ "Ilya Novosyolov",
+ "Ioram Gordadze",
+ "Ivan Nezhdanov",
+ "J. Pereira Rocha",
+ "J.A.J. Vermeulen",
+ "Jack Jennings",
+ "James M Cape",
+ "James Mason",
+ "James",
+ "Jan Dudulski",
+ "Jan-Christoph Borchardt",
+ "Jason Carey",
+ "Jason D Levine",
+ "Jason R Anderson",
+ "Jason Scurtu",
+ "Javier Jardón",
+ "Javier Monteagudo",
+ "Jean-François Fortin Tam",
+ "Jeff Waugh",
+ "Jeffrey Dorrycott",
+ "Jesse van den Kieboom",
+ "Jim Campbell",
+ "Jiri Eischmann",
+ "Joakim Söderlund",
+ "Joaquin Mendez",
+ "Johan Dahlin",
+ "John Gary Billings",
+ "John M Carr",
+ "John Palmieri",
+ "Jonathan Lane",
+ "Jonathan Lestrelin",
+ "Jonathan Zuñiga Juarez",
+ "Jonathon Jongsma",
+ "Jorge Rodriguez Flores Esp",
+ "Joseph Hain",
+ "Juan Jose Marin Martinez",
+ "Jugoslav Gacas",
+ "Julien Girardin",
+ "Jussi Henrik Kukkonen",
+ "Justin D Kruger",
+ "Justin Roth",
+ "Justyn Butler",
+ "Katrin Leinweber",
+ "Keith Tokash",
+ "Kenneth Nielsen",
+ "Khalid Eldehairy",
+ "Kris Thomsen",
+ "Lapo Calamandrei",
+ "Lars Uebernickel",
+ "Laurent Mouillart",
+ "Le Guevel Gwendal",
+ "Leif Gruenwoldt",
+ "Lionel Landwerlin",
+ "Logan VanCuren",
+ "Lucas Almeida Rocha",
+ "Lukasz Ochoda",
+ "Luke Gaudreau",
+ "M C V Crouch",
+ "M. Aurélien Couderc",
+ "Mac Baker",
+ "Maciej M Piechotka",
+ "Magnun Leno Silva",
+ "Marc Andre Lureau",
+ "Marc Thomas",
+ "Marcio Sousa Rocha",
+ "Marco Barisione",
+ "Marcus Husar",
+ "Marcus Lundblad",
+ "Marek Suchánek",
+ "Marina Zhurakhinskaya",
+ "Mario Sanchez-Prada",
+ "Marius Heidenreich",
+ "Marius Mather",
+ "Markus Berg",
+ "Mart Roosmaa",
+ "Martin A Stembel",
+ "Martin Andersson",
+ "Martin Blanchard",
+ "Martin C Foster",
+ "Martin Unzner",
+ "Matej Smid",
+ "Mathieu Bridon",
+ "Matthew Nicholson",
+ "Mattias Bengtsson",
+ "Max Whittingham",
+ "Maxim Yaskevich",
+ "Michael Catanzaro",
+ "Michael Grundy",
+ "Michael Hill",
+ "Michael Ivanov",
+ "Michael Kuhn",
+ "Michael Mansell",
+ "Michael S DePaulo",
+ "Michael Scofield",
+ "Michel Alexandre Salim",
+ "Miguel e dos Santos",
+ "Mikel Olasagasti Uranga",
+ "Mikhail Feshchenko",
+ "Molly Shelestak",
+ "Nasser Alshammari",
+ "Nathan Samson",
+ "Neil Stalker",
+ "Neils Nesse",
+ "Nelson Jesus Benitez Leon",
+ "Nicholas E Richards",
+ "Nicholas George",
+ "Nick Melnick",
+ "Niclas Moeslund Overby",
+ "Nicola Mazbar",
+ "Nicolas Jeker",
+ "Niklas Rosenqvist",
+ "Nikola Trifunovic",
+ "Nil Gradisnik",
+ "Nirbheek Chauhan",
+ "Olav Vitters",
+ "Oliver Propst",
+ "Olivier Crete",
+ "Ondřej Holý",
+ "Ondřej Tůma",
+ "Owen Taylor",
+ "P Tunnell Wilson",
+ "P.F. Mulder",
+ "Pacaud Emmanuel",
+ "Pakkanen Jussi T",
+ "Paolo Borelli",
+ "Pascal Garber",
+ "Patrick Griffis",
+ "Patrick Wspanialy",
+ "Patrik Nilsson",
+ "Patrizio Bruno",
+ "Paul R Martin",
+ "Pedro J Ayala Gomariz",
+ "Perry L Peters",
+ "Peter Baumgarten",
+ "Peter Cornelis",
+ "Peter J Shinners",
+ "Peter Weber",
+ "Philip Corbett",
+ "Philip F Chimento",
+ "Philip J Freeman",
+ "Philip Whitfield",
+ "Piotr Zurek",
+ "R A McQueen",
+ "R A McQueen",
+ "RM van Schouwen",
+ "Radosław Sierbiński",
+ "Ray Strode",
+ "Remco Kranenburg",
+ "Remi Grolleau",
+ "Rickard Johansson",
+ "Robert Carr",
+ "Robert Taylor",
+ "Roberto Clapis",
+ "Rodolphe PP",
+ "Rory MacQueen",
+ "Rosanna Blandford",
+ "Ross N Gardiner",
+ "Rouchon Jean-Noel",
+ "Rui Paulo Barreira",
+ "Russell Cox",
+ "Ryan Hartlage",
+ "Ryan Lerch",
+ "Rémi Lauzier",
+ "S Axon",
+ "Sajid Badi-uz-zaman",
+ "Samuel B Thursfield",
+ "Samuel Gyger",
+ "Sasan Namiranian",
+ "Saul Vargas Sandoval",
+ "Sebastian Droege",
+ "Shapor Naghibzadeh",
+ "Shawn Ferris",
+ "Shlomo Choina",
+ "Shuji Narazaki",
+ "Simon Roesch",
+ "Sriram Ramkrishna",
+ "Stefi T Petit",
+ "Stephany Wilkes",
+ "Stephen Genusa",
+ "Stephen Shaw",
+ "Steve Z McCauley",
+ "Steven J Herber",
+ "Steven W Brown",
+ "Steven Wills",
+ "Stewart Webb",
+ "Stuart Ellis",
+ "Stéphane Démurget",
+ "Stéphane Maniaci",
+ "Søren Hauberg",
+ "Tadej Janež",
+ "Ted Hennicke",
+ "Thibault Saunier",
+ "Thomas Andersen",
+ "Thomas Maffia",
+ "Thomas McDonald",
+ "Tom Erik Gundersen",
+ "Tomas Peterka",
+ "Tommi Tauriainen",
+ "Tomáš Krchňák",
+ "Tomáš Popela",
+ "Toni Willberg",
+ "Torsten Scholak",
+ "Travis Hartwell",
+ "Tyler J. Brock",
+ "Uwe Hametner",
+ "Vadzim Rutkouski",
+ "Valter Schütz",
+ "WP MANLEY",
+ "WP Manley",
+ "Wee Weea",
+ "Wesley Wiser",
+ "Will Binns-Smith",
+ "William Hoffmann",
+ "William J Thompson",
+ "William J Thompson",
+ "William Jon McCann",
+ "William R Lachance",
+ "Z Jedrzejewski-Szmek",
+ "adam820",
+ "arclnx",
+ "aurelien.busi",
+ "carwyn",
+ "d.westerik",
+ "daniel.fontaine",
+ "david odenwald",
+ "dchristidis",
+ "demirtas burakk",
+ "eik.w1911",
+ "eliasdorneles",
+ "elken.tdos",
+ "fafatheone",
+ "florian.muellner",
+ "gerald.b.nunn",
+ "gyger",
+ "ich",
+ "ideasman42",
+ "jimmac",
+ "jnoel",
+ "joannis.orlandos",
+ "joaquin8mendez",
+ "joel",
+ "juanjomarin96",
+ "kamilprusko",
+ "kenneth",
+ "kesmarag",
+ "kevin",
+ "kidoz",
+ "kuba",
+ "lovenemesis",
+ "luke.a.morton",
+ "madstitz",
+ "marc.chocolat",
+ "mariospr",
+ "markpariente",
+ "matthias.clasen",
+ "maxlupo",
+ "mcatanzaro",
+ "muflone",
+ "nils.werner",
+ "otaylor",
+ "pedroclg",
+ "peterldev94",
+ "pizzamartijn",
+ "pseus7+indiegogo",
+ "public228",
+ "ray strode",
+ "ross",
+ "sandquist",
+ "scottm2031",
+ "sebastien lafargue",
+ "sebastien.wilmet",
+ "sfs",
+ "slyon",
+ "swalf",
+ "sylvain.pasche",
+ "tglman",
+ "theo",
+ "tommaso.visconti",
+ "vamega",
+ "verduler",
+ "vperetokin",
+ "w.vollprecht",
+ NULL
+};
+
+G_END_DECLS
+
+#endif /* IDE_APPLICATION_CREDITS_H */
diff --git a/src/greeter/gb-greeter-pill-box.h b/libide/ide-application-private.h
similarity index 51%
copy from src/greeter/gb-greeter-pill-box.h
copy to libide/ide-application-private.h
index 394a333..cf3856f 100644
--- a/src/greeter/gb-greeter-pill-box.h
+++ b/libide/ide-application-private.h
@@ -1,4 +1,4 @@
-/* gb-greeter-pill-box.h
+/* gb-application-private.h
*
* Copyright (C) 2015 Christian Hergert <christian hergert me>
*
@@ -16,22 +16,35 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef GB_GREETER_PILL_BOX_H
-#define GB_GREETER_PILL_BOX_H
+#ifndef IDE_APPLICATION_PRIVATE_H
+#define IDE_APPLICATION_PRIVATE_H
#include <gtk/gtk.h>
+#include <gio/gio.h>
+#include <libpeas/peas.h>
-G_BEGIN_DECLS
-
-#define GB_TYPE_GREETER_PILL_BOX (gb_greeter_pill_box_get_type())
+#include "ide-keybindings.h"
+#include "ide-recent-projects.h"
+#include "ide-worker-manager.h"
-G_DECLARE_FINAL_TYPE (GbGreeterPillBox, gb_greeter_pill_box, GB, GREETER_PILL_BOX, GtkEventBox)
+G_BEGIN_DECLS
-GtkWidget *gb_greeter_pill_box_new (const gchar *label);
-const gchar *gb_greeter_pill_box_get_label (GbGreeterPillBox *self);
-void gb_greeter_pill_box_set_label (GbGreeterPillBox *self,
- const gchar *label);
+struct _IdeApplication
+{
+ GtkApplication parent_instance;
+
+ gchar *argv0;
+ gchar *dbus_address;
+ PeasExtensionSet *extensions;
+ GtkWindowGroup *greeter_group;
+ IdeKeybindings *keybindings;
+ GtkWindow *preferences_window;
+ IdeRecentProjects *recent_projects;
+ GDateTime *startup_time;
+ gchar *type;
+ IdeWorkerManager *worker_manager;
+};
G_END_DECLS
-#endif /* GB_GREETER_PILL_BOX_H */
+#endif /* GB_APPLICATION_PRIVATE_H */
diff --git a/libide/ide-application.c b/libide/ide-application.c
new file mode 100644
index 0000000..faab43a
--- /dev/null
+++ b/libide/ide-application.c
@@ -0,0 +1,1002 @@
+/* ide-application.c
+ *
+ * Copyright (C) 2014 Christian Hergert <christian hergert me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-application"
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef __linux
+# include <sys/prctl.h>
+#endif
+
+#include <glib/gi18n.h>
+#include <gtksourceview/gtksource.h>
+#include <libgit2-glib/ggit.h>
+
+#include "ide-application.h"
+#include "ide-application-actions.h"
+#include "ide-application-addin.h"
+#include "ide-application-private.h"
+#include "ide-css-provider.h"
+#include "ide-debug.h"
+#include "ide-internal.h"
+#include "ide-file.h"
+#include "ide-log.h"
+#include "ide-macros.h"
+#include "ide-resources.h"
+#include "ide-vcs.h"
+#include "ide-workbench.h"
+#include "ide-worker.h"
+
+#include "modeline-parser.h"
+
+G_DEFINE_TYPE (IdeApplication, ide_application, GTK_TYPE_APPLICATION)
+
+static gboolean
+ide_application_can_load_plugin (IdeApplication *self,
+ PeasPluginInfo *plugin_info)
+{
+ const gchar *plugin_name;
+
+ g_assert (IDE_IS_APPLICATION (self));
+ g_assert (plugin_info != NULL);
+
+ /* Currently we only allow in-tree plugins */
+ if (!peas_plugin_info_is_builtin (plugin_info))
+ return FALSE;
+
+ plugin_name = peas_plugin_info_get_module_name (plugin_info);
+ if (ide_str_equal0 (plugin_name, self->type))
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+ide_application_load_plugins (IdeApplication *self)
+{
+ PeasEngine *engine = peas_engine_get_default ();
+ const GList *list;
+
+ peas_engine_enable_loader (engine, "python3");
+
+ if (g_getenv ("BUiLDER_IN_TREE_PLUGINS") != NULL)
+ {
+ GDir *dir;
+
+ g_irepository_require_private (g_irepository_get_default (),
+ BUILDDIR"/libide",
+ "Ide", "1.0", 0, NULL);
+
+ if ((dir = g_dir_open (BUILDDIR"/plugins", 0, NULL)))
+ {
+ const gchar *name;
+
+ while ((name = g_dir_read_name (dir)))
+ {
+ gchar *path;
+
+ path = g_build_filename (BUILDDIR, "plugins", name, NULL);
+ peas_engine_prepend_search_path (engine, path, path);
+ g_free (path);
+ }
+
+ g_dir_close (dir);
+ }
+ }
+ else
+ {
+ peas_engine_prepend_search_path (engine,
+ PACKAGE_LIBDIR"/gnome-builder/plugins",
+ PACKAGE_DATADIR"/gnome-builder/plugins");
+ }
+
+ list = peas_engine_get_plugin_list (engine);
+
+ for (; list; list = list->next)
+ {
+ if (ide_application_can_load_plugin (self, list->data))
+ peas_engine_load_plugin (engine, list->data);
+ }
+}
+
+static gboolean
+ide_application_is_worker (IdeApplication *self)
+{
+ g_assert (IDE_IS_APPLICATION (self));
+
+ return (self->type != NULL) && (self->dbus_address != NULL);
+}
+
+static void
+ide_application_load_worker (IdeApplication *self)
+{
+ g_autoptr(GDBusConnection) connection = NULL;
+ PeasEngine *engine;
+ PeasPluginInfo *plugin_info;
+ GError *error = NULL;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_APPLICATION (self));
+ g_assert (ide_application_is_worker (self));
+
+#ifdef __linux
+ /* Ensure we are killed with our parent */
+ prctl (PR_SET_PDEATHSIG, 15);
+#endif
+
+ IDE_TRACE_MSG ("Connecting to %s", self->dbus_address);
+
+ connection = g_dbus_connection_new_for_address_sync (self->dbus_address,
+ (G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
+ G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING),
+ NULL, NULL, &error);
+
+ if (error != NULL)
+ {
+ g_error ("DBus failure: %s", error->message);
+ g_clear_error (&error);
+ IDE_EXIT;
+ }
+
+ g_assert (G_IS_DBUS_CONNECTION (connection));
+
+ engine = peas_engine_get_default ();
+ plugin_info = peas_engine_get_plugin_info (engine, self->type);
+
+ if ((plugin_info != NULL) && peas_plugin_info_is_loaded (plugin_info))
+ {
+ PeasExtension *exten;
+
+ exten = peas_engine_create_extension (engine, plugin_info, IDE_TYPE_WORKER, NULL);
+
+ if (exten != NULL)
+ {
+ ide_worker_register_service (IDE_WORKER (exten), connection);
+ IDE_GOTO (success);
+ }
+ }
+
+ g_error ("Failed to create \"%s\" worker.", self->type);
+
+ IDE_EXIT;
+
+success:
+ g_application_hold (G_APPLICATION (self));
+ g_dbus_connection_start_message_processing (connection);
+
+ IDE_EXIT;
+}
+
+static void
+ide_application_setup_search_paths (void)
+{
+ GtkSourceStyleSchemeManager *style_scheme_manager;
+ static gboolean initialized;
+
+ if (initialized)
+ return;
+
+ style_scheme_manager = gtk_source_style_scheme_manager_get_default ();
+ gtk_source_style_scheme_manager_append_search_path (style_scheme_manager,
+ PACKAGE_DATADIR"/gtksourceview-3.0/styles/");
+ initialized = TRUE;
+}
+
+/**
+ * ide_application_make_skeleton_dirs:
+ * @self: A #IdeApplication.
+ *
+ * Creates all the directories we might need later. Simpler to just ensure they
+ * are created during startup.
+ */
+static void
+ide_application_make_skeleton_dirs (IdeApplication *self)
+{
+ gchar *path;
+
+ g_return_if_fail (IDE_IS_APPLICATION (self));
+
+ path = g_build_filename (g_get_user_data_dir (),
+ "gnome-builder",
+ NULL);
+ g_mkdir_with_parents (path, 0750);
+ g_free (path);
+
+ path = g_build_filename (g_get_user_config_dir (),
+ "gnome-builder",
+ NULL);
+ g_mkdir_with_parents (path, 0750);
+ g_free (path);
+
+ path = g_build_filename (g_get_user_config_dir (),
+ "gnome-builder",
+ "snippets",
+ NULL);
+ g_mkdir_with_parents (path, 0750);
+ g_free (path);
+}
+
+static void
+ide_application_register_theme_overrides (IdeApplication *application)
+{
+ g_autoptr(GSettings) settings = NULL;
+ g_autoptr(GtkCssProvider) provider = NULL;
+ GtkSettings *gtk_settings;
+ GdkScreen *screen;
+
+ IDE_ENTRY;
+
+ gtk_icon_theme_add_resource_path (gtk_icon_theme_get_default (), "/org/gnome/builder/icons/");
+
+ provider = ide_css_provider_new ();
+ screen = gdk_screen_get_default ();
+ gtk_style_context_add_provider_for_screen (screen, GTK_STYLE_PROVIDER (provider),
+ GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+
+ gtk_settings = gtk_settings_get_for_screen (screen);
+ settings = g_settings_new ("org.gnome.builder");
+ g_settings_bind (settings, "night-mode", gtk_settings, "gtk-application-prefer-dark-theme",
+ G_SETTINGS_BIND_DEFAULT);
+
+ IDE_EXIT;
+}
+
+static void
+ide_application_load_keybindings (IdeApplication *self)
+{
+ g_autoptr(GSettings) settings = NULL;
+ g_autofree gchar *name = NULL;
+
+ /* TODO: Move this to keybindings */
+ static const struct { gchar *name; gchar *binding; } shared_bindings[] = {
+ { "workbench.show-left-pane", "F9" },
+ { "workbench.show-right-pane", "<shift>F9" },
+ { "workbench.show-bottom-pane", "<ctrl>F9" },
+ { "workbench.toggle-panels", "<ctrl><shift>F9" },
+ { "workbench.focus-left", "<ctrl>grave" },
+ { "workbench.focus-right", "<ctrl>9" },
+ { "workbench.focus-stack(1)", "<ctrl>1" },
+ { "workbench.focus-stack(2)", "<ctrl>2" },
+ { "workbench.focus-stack(3)", "<ctrl>3" },
+ { "workbench.focus-stack(4)", "<ctrl>4" },
+ { "workbench.focus-stack(5)", "<ctrl>5" },
+ { "workbench.show-gear-menu", "F10" },
+ { "workbench.global-search", "<ctrl>period" },
+ { "app.preferences", "<Primary>comma" },
+ { "app.shortcuts", "<ctrl>question" },
+ { "workbench.new-document", "<ctrl>n" },
+ { "workbench.open-document", "<ctrl>o" },
+ { NULL }
+ };
+ gsize i;
+
+ g_assert (IDE_IS_APPLICATION (self));
+
+ settings = g_settings_new ("org.gnome.builder.editor");
+ name = g_settings_get_string (settings, "keybindings");
+ self->keybindings = ide_keybindings_new (GTK_APPLICATION (self), name);
+ g_settings_bind (settings, "keybindings", self->keybindings, "mode", G_SETTINGS_BIND_GET);
+
+ for (i = 0; shared_bindings [i].name; i++)
+ {
+ const gchar *accels[2] = { shared_bindings [i].binding, NULL };
+ gtk_application_set_accels_for_action (GTK_APPLICATION (self),
+ shared_bindings [i].name,
+ accels);
+ }
+}
+
+static IdeWorkbench *
+ide_application_find_workbench_for_file (IdeApplication *self,
+ GFile *file)
+{
+ GList *iter;
+ GList *workbenches;
+
+ g_assert (IDE_IS_APPLICATION (self));
+ g_assert (G_IS_FILE (file));
+
+ workbenches = gtk_application_get_windows (GTK_APPLICATION (self));
+
+ /*
+ * Find the a project that contains this file in its working directory.
+ */
+ for (iter = workbenches; iter; iter = iter->next)
+ {
+ if (IDE_IS_WORKBENCH (iter->data))
+ {
+ IdeWorkbench *workbench = iter->data;
+ g_autofree gchar *relpath = NULL;
+ IdeContext *context;
+ IdeVcs *vcs;
+ GFile *workdir;
+
+ context = ide_workbench_get_context (workbench);
+ vcs = ide_context_get_vcs (context);
+ workdir = ide_vcs_get_working_directory (vcs);
+
+ relpath = g_file_get_relative_path (workdir, file);
+
+ if (relpath != NULL)
+ return workbench;
+ }
+ }
+
+ /*
+ * No matches found, take the first workbench we find.
+ */
+ for (iter = workbenches; iter; iter = iter->next)
+ if (IDE_IS_WORKBENCH (iter->data))
+ return iter->data;
+
+ return NULL;
+}
+
+static void
+ide_application__context_new_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = user_data;
+ g_autoptr(IdeContext) context = NULL;
+ IdeApplication *self;
+ IdeWorkbench *workbench;
+ GPtrArray *ar;
+ GError *error = NULL;
+ gsize i;
+
+ g_assert (G_IS_TASK (task));
+
+ self = g_task_get_source_object (task);
+ ar = g_task_get_task_data (task);
+
+ g_assert (IDE_IS_APPLICATION (self));
+ g_assert (ar);
+
+ context = ide_context_new_finish (result, &error);
+
+ if (!context)
+ {
+ g_task_return_error (task, error);
+ goto cleanup;
+ }
+
+ {
+ IdeVcs *vcs;
+ GFile *workdir;
+ g_autofree gchar *path = NULL;
+
+ vcs = ide_context_get_vcs (context);
+ workdir = ide_vcs_get_working_directory (vcs);
+ path = g_file_get_path (workdir);
+
+ g_debug ("Project working directory: %s", path);
+ }
+
+ workbench = g_object_new (IDE_TYPE_WORKBENCH,
+ "application", self,
+ "context", context,
+ NULL);
+
+ for (i = 0; i < ar->len; i++)
+ {
+ GFile *file;
+
+ file = g_ptr_array_index (ar, i);
+ g_assert (G_IS_FILE (file));
+
+ //ide_workbench_open (workbench, file);
+ }
+
+ gtk_window_present (GTK_WINDOW (workbench));
+
+ g_task_return_boolean (task, TRUE);
+
+cleanup:
+ g_application_unmark_busy (G_APPLICATION (self));
+ g_application_release (G_APPLICATION (self));
+}
+
+/**
+ * ide_application_open_project_async:
+ * @self: A #IdeApplication.
+ * @file: A #GFile.
+ * @additional_files: (element-type GFile) (nullable): A #GPtrArray of #GFile or %NULL.
+ *
+ */
+void
+ide_application_open_project_async (IdeApplication *self,
+ GFile *file,
+ GPtrArray *additional_files,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GFile) directory = NULL;
+ g_autoptr(GTask) task = NULL;
+ g_autoptr(GPtrArray) ar = NULL;
+ GList *windows;
+ GList *iter;
+
+ g_return_if_fail (IDE_IS_APPLICATION (self));
+ g_return_if_fail (G_IS_FILE (file));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+
+ windows = gtk_application_get_windows (GTK_APPLICATION (self));
+
+ for (iter = windows; iter; iter = iter->next)
+ {
+ if (IDE_IS_WORKBENCH (iter->data))
+ {
+ IdeContext *context;
+
+ context = ide_workbench_get_context (iter->data);
+
+ if (context != NULL)
+ {
+ GFile *project_file;
+
+ project_file = ide_context_get_project_file (context);
+
+ if (g_file_equal (file, project_file))
+ {
+ gtk_window_present (iter->data);
+ g_task_return_boolean (task, TRUE);
+ return;
+ }
+ }
+ }
+ }
+
+ if (additional_files)
+ ar = g_ptr_array_ref (additional_files);
+ else
+ ar = g_ptr_array_new ();
+
+ g_task_set_task_data (task, g_ptr_array_ref (ar), (GDestroyNotify)g_ptr_array_unref);
+
+ if (g_file_query_file_type (file, 0, NULL) == G_FILE_TYPE_DIRECTORY)
+ directory = g_object_ref (file);
+ else
+ directory = g_file_get_parent (file);
+
+ g_application_mark_busy (G_APPLICATION (self));
+ g_application_hold (G_APPLICATION (self));
+
+ ide_context_new_async (directory,
+ NULL,
+ ide_application__context_new_cb,
+ g_object_ref (task));
+}
+
+gboolean
+ide_application_open_project_finish (IdeApplication *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ GTask *task = (GTask *)result;
+
+ g_return_val_if_fail (IDE_IS_APPLICATION (self), FALSE);
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+ g_return_val_if_fail (G_IS_TASK (task), FALSE);
+
+ return g_task_propagate_boolean (task, error);
+}
+
+static void
+ide_application_open (GApplication *application,
+ GFile **files,
+ gint n_files,
+ const gchar *hint)
+{
+ IdeApplication *self = (IdeApplication *)application;
+ IdeWorkbench *workbench;
+ g_autoptr(GPtrArray) ar = NULL;
+ guint i;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_APPLICATION (self));
+
+ /*
+ * Try to open the files using an existing workbench.
+ */
+ for (i = 0; i < n_files; i++)
+ {
+ GFile *file = files [i];
+
+ g_assert (G_IS_FILE (file));
+
+ workbench = ide_application_find_workbench_for_file (self, file);
+
+ if (workbench != NULL)
+ {
+ //ide_workbench_open (workbench, file);
+ gtk_window_present (GTK_WINDOW (workbench));
+ continue;
+ }
+
+ if (!ar)
+ ar = g_ptr_array_new_with_free_func (g_object_unref);
+ g_ptr_array_add (ar, g_object_ref (file));
+ }
+
+ /*
+ * No workbench found for these files, let's create one!
+ */
+ if (ar && ar->len)
+ {
+ GFile *file = g_ptr_array_index (ar, 0);
+
+ ide_application_open_project_async (self, file, ar, NULL, NULL, NULL);
+ }
+
+ IDE_EXIT;
+}
+
+void
+ide_application_show_projects_window (IdeApplication *self)
+{
+#if 0
+ IdeProjectsDialog *window;
+ GList *windows;
+
+ g_assert (IDE_IS_APPLICATION (self));
+
+ windows = gtk_application_get_windows (GTK_APPLICATION (self));
+
+ for (; windows; windows = windows->next)
+ {
+ if (IDE_IS_GREETER_WINDOW (windows->data))
+ {
+ gtk_window_present (windows->data);
+ return;
+ }
+ }
+
+ if (self->recent_projects == NULL)
+ {
+ self->recent_projects = ide_recent_projects_new ();
+ ide_recent_projects_discover_async (self->recent_projects, NULL, NULL, NULL);
+ }
+
+ window = g_object_new (IDE_TYPE_GREETER_WINDOW,
+ "application", self,
+ "recent-projects", self->recent_projects,
+ NULL);
+ gtk_window_group_add_window (self->greeter_group, GTK_WINDOW (window));
+ gtk_window_present (GTK_WINDOW (window));
+#endif
+}
+
+static void
+ide_application_activate (GApplication *application)
+{
+ IdeApplication *self = (IdeApplication *)application;
+ IdeWorkbench *workbench;
+ GList *list;
+
+ g_assert (IDE_IS_APPLICATION (self));
+
+ if (ide_application_is_worker (self))
+ {
+ ide_application_load_worker (self);
+ return;
+ }
+
+ list = gtk_application_get_windows (GTK_APPLICATION (application));
+
+ for (; list; list = list->next)
+ {
+ if (IDE_IS_WORKBENCH (list->data))
+ {
+ gtk_window_present (GTK_WINDOW (list->data));
+ return;
+ }
+ }
+
+ workbench = g_object_new (IDE_TYPE_WORKBENCH,
+ "application", self,
+ NULL);
+ gtk_window_maximize (GTK_WINDOW (workbench));
+ gtk_window_present (GTK_WINDOW (workbench));
+}
+
+static void
+ide_application__extension_added (PeasExtensionSet *extensions,
+ PeasPluginInfo *plugin_info,
+ IdeApplicationAddin *addin,
+ IdeApplication *self)
+{
+ g_assert (IDE_IS_APPLICATION (self));
+ g_assert (plugin_info != NULL);
+ g_assert (IDE_IS_APPLICATION_ADDIN (addin));
+ g_assert (PEAS_IS_EXTENSION_SET (extensions));
+
+ ide_application_addin_load (addin, self);
+}
+
+static void
+ide_application__extension_removed (PeasExtensionSet *extensions,
+ PeasPluginInfo *plugin_info,
+ IdeApplicationAddin *addin,
+ IdeApplication *self)
+{
+ g_assert (IDE_IS_APPLICATION (self));
+ g_assert (plugin_info != NULL);
+ g_assert (IDE_IS_APPLICATION_ADDIN (addin));
+ g_assert (PEAS_IS_EXTENSION_SET (extensions));
+
+ ide_application_addin_unload (addin, self);
+}
+
+static void
+ide_application_load_addins (IdeApplication *self)
+{
+ PeasEngine *engine;
+
+ g_assert (IDE_IS_APPLICATION (self));
+
+ engine = peas_engine_get_default ();
+
+ self->extensions = peas_extension_set_new (engine, IDE_TYPE_APPLICATION_ADDIN, NULL);
+
+ peas_extension_set_foreach (self->extensions,
+ (PeasExtensionSetForeachFunc)ide_application__extension_added,
+ self);
+
+ g_signal_connect_object (self->extensions,
+ "extension-added",
+ G_CALLBACK (ide_application__extension_added),
+ self,
+ 0);
+
+ g_signal_connect_object (self->extensions,
+ "extension-removed",
+ G_CALLBACK (ide_application__extension_removed),
+ self,
+ 0);
+}
+
+static void
+ide_application_startup (GApplication *app)
+{
+ IdeApplication *self = (IdeApplication *)app;
+ GgitFeatureFlags ggit_flags;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_APPLICATION (self));
+
+ self->startup_time = g_date_time_new_now_utc ();
+
+ g_resources_register (ide_get_resource ());
+
+ g_application_set_resource_base_path (app, "/org/gnome/builder");
+
+ g_irepository_prepend_search_path (PACKAGE_LIBDIR"/gnome-builder/girepository-1.0");
+
+ if (!ide_application_is_worker (self))
+ self->greeter_group = gtk_window_group_new ();
+
+ _ide_battery_monitor_init ();
+ _ide_thread_pool_init (ide_application_is_worker (self));
+
+ modeline_parser_init ();
+
+ ggit_init ();
+
+ ggit_flags = ggit_get_features ();
+
+ if ((ggit_flags & GGIT_FEATURE_THREADS) == 0)
+ {
+ g_error (_("Builder requires libgit2-glib with threading support."));
+ exit (EXIT_FAILURE);
+ }
+
+ if ((ggit_flags & GGIT_FEATURE_SSH) == 0)
+ {
+ g_error (_("Builder requires libgit2-glib with SSH support."));
+ exit (EXIT_FAILURE);
+ }
+
+ G_APPLICATION_CLASS (ide_application_parent_class)->startup (app);
+
+ if (!ide_application_is_worker (self))
+ {
+ ide_application_make_skeleton_dirs (self);
+ ide_application_actions_init (self);
+ ide_application_register_theme_overrides (self);
+ ide_application_setup_search_paths ();
+ ide_application_load_keybindings (self);
+ ide_application_load_plugins (self);
+ ide_application_load_addins (self);
+ }
+
+ IDE_EXIT;
+}
+
+static gboolean
+ide_application_increase_verbosity (void)
+{
+ ide_log_increase_verbosity ();
+ return TRUE;
+}
+
+static gint
+ide_application_handle_local_options (GApplication *app,
+ GVariantDict *options)
+{
+ if (g_variant_dict_contains (options, "version"))
+ {
+ g_print ("%s - Version %s\n", g_get_application_name (), VERSION);
+ return 0;
+ }
+
+ if (g_variant_dict_contains (options, "standalone") || g_variant_dict_contains (options, "type"))
+ {
+ GApplicationFlags flags;
+
+ flags = g_application_get_flags (app);
+ g_application_set_flags (app, flags | G_APPLICATION_NON_UNIQUE);
+ }
+
+ return -1;
+}
+
+static gboolean
+ide_application_local_command_line (GApplication *application,
+ gchar ***arguments,
+ int *exit_status)
+{
+ IdeApplication *self = (IdeApplication *)application;
+
+ g_assert (IDE_IS_APPLICATION (self));
+ g_assert (arguments != NULL);
+ g_assert (*arguments != NULL);
+ g_assert (exit_status != NULL);
+
+ self->argv0 = g_strdup ((*arguments) [0]);
+
+ return G_APPLICATION_CLASS (ide_application_parent_class)->
+ local_command_line (application, arguments, exit_status);
+}
+
+static void
+ide_application_finalize (GObject *object)
+{
+ IdeApplication *self = (IdeApplication *)object;
+
+ IDE_ENTRY;
+
+ g_clear_object (&self->extensions);
+ g_clear_pointer (&self->startup_time, g_date_time_unref);
+ g_clear_pointer (&self->argv0, g_free);
+ g_clear_object (&self->keybindings);
+ g_clear_object (&self->recent_projects);
+ g_clear_object (&self->greeter_group);
+
+ G_OBJECT_CLASS (ide_application_parent_class)->finalize (object);
+
+ IDE_EXIT;
+}
+
+static void
+ide_application_class_init (IdeApplicationClass *klass)
+{
+ GApplicationClass *app_class = G_APPLICATION_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ IDE_ENTRY;
+
+ object_class->finalize = ide_application_finalize;
+
+ app_class->activate = ide_application_activate;
+ app_class->startup = ide_application_startup;
+ app_class->open = ide_application_open;
+ app_class->local_command_line = ide_application_local_command_line;
+ app_class->handle_local_options = ide_application_handle_local_options;
+
+ IDE_EXIT;
+}
+
+static void
+ide_application_init (IdeApplication *app)
+{
+ GOptionEntry options[] = {
+ { "standalone",
+ 's',
+ G_OPTION_FLAG_IN_MAIN,
+ G_OPTION_ARG_NONE,
+ NULL,
+ N_("Run Builder in standalone mode") },
+
+ { "version",
+ 0,
+ G_OPTION_FLAG_IN_MAIN,
+ G_OPTION_ARG_NONE,
+ NULL,
+ N_("Show the application's version") },
+
+ { "verbose",
+ 'v',
+ G_OPTION_FLAG_NO_ARG | G_OPTION_FLAG_IN_MAIN | G_OPTION_FLAG_HIDDEN,
+ G_OPTION_ARG_CALLBACK,
+ ide_application_increase_verbosity,
+ N_("Increase verbosity. May be specified multiple times.") },
+
+ { "dbus-address",
+ 0,
+ G_OPTION_FLAG_HIDDEN,
+ G_OPTION_ARG_STRING,
+ &app->dbus_address,
+ N_("The DBus server address for which to connect.") },
+
+ { "type",
+ 0,
+ G_OPTION_FLAG_HIDDEN,
+ G_OPTION_ARG_STRING,
+ &app->type,
+ N_("The type of plugin worker process to run.") },
+
+ { NULL }
+ };
+
+ IDE_ENTRY;
+
+ g_application_add_main_option_entries (G_APPLICATION (app), options);
+
+ IDE_EXIT;
+}
+
+GDateTime *
+ide_application_get_startup_time (IdeApplication *self)
+{
+ g_return_val_if_fail (IDE_IS_APPLICATION (self), NULL);
+
+ return self->startup_time;
+}
+
+const gchar *
+ide_application_get_keybindings_mode (IdeApplication *self)
+{
+ g_return_val_if_fail (IDE_IS_APPLICATION (self), NULL);
+
+ return ide_keybindings_get_mode (self->keybindings);
+}
+
+static void
+ide_application_get_worker_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeWorkerManager *worker_manager = (IdeWorkerManager *)object;
+ g_autoptr(GTask) task = user_data;
+ GError *error = NULL;
+ GDBusProxy *proxy;
+
+ g_assert (IDE_IS_WORKER_MANAGER (worker_manager));
+
+ proxy = ide_worker_manager_get_worker_finish (worker_manager, result, &error);
+
+ if (proxy == NULL)
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task, proxy, g_object_unref);
+}
+
+/**
+ * ide_application_get_worker_async:
+ * @self: A #IdeApplication
+ * @plugin_name: The name of the plugin.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback or %NULL.
+ * @user_data: user data for @callback.
+ *
+ * Asynchronously requests a #GDBusProxy to a service provided in a worker
+ * process. The worker should be an #IdeWorker implemented by the plugin named
+ * @plugin_name. The #IdeWorker is responsible for created both the service
+ * registered on the bus and the proxy to it.
+ *
+ * The #IdeApplication is responsible for spawning a subprocess for the worker.
+ *
+ * @callback should call ide_application_get_worker_finish() with the result
+ * provided to retrieve the result.
+ */
+void
+ide_application_get_worker_async (IdeApplication *self,
+ const gchar *plugin_name,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task = NULL;
+
+ g_return_if_fail (IDE_IS_APPLICATION (self));
+ g_return_if_fail (plugin_name != NULL);
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ if (self->worker_manager == NULL)
+ self->worker_manager = ide_worker_manager_new (self->argv0);
+
+ task = g_task_new (self, cancellable, callback, user_data);
+
+ ide_worker_manager_get_worker_async (self->worker_manager,
+ plugin_name,
+ cancellable,
+ ide_application_get_worker_cb,
+ task);
+}
+
+/**
+ * ide_application_get_worker_finish:
+ * @self: A #IdeApplication.
+ * @result: A #GAsyncResult
+ * @error: a location for a #GError, or %NULL.
+ *
+ * Completes an asynchronous request to get a proxy to a worker process.
+ *
+ * Returns: (transfer full): A #GDBusProxy or %NULL.
+ */
+GDBusProxy *
+ide_application_get_worker_finish (IdeApplication *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ GTask *task = (GTask *)result;
+
+ g_return_val_if_fail (IDE_IS_APPLICATION (self), NULL);
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
+ g_return_val_if_fail (G_IS_TASK (task), NULL);
+
+ return g_task_propagate_pointer (task, error);
+}
+
+/**
+ * ide_application_get_recent_projects:
+ * @self: An #IdeApplication.
+ *
+ * This method will retreive an #IdeRecentProjects for the application that
+ * represents recent and discover projects on the system. The first time
+ * the #IdeRecentProjects is loaded, discovery of projects will occur. There
+ * is no need to call ide_recent_projects_discover_async().
+ *
+ * If you would like to display a spinner while discovery is in process, simply
+ * connect to the #IdeRecentProjects:busy: property notification.
+ *
+ * Returns: (transfer none): An #IdeRecentProjects.
+ */
+IdeRecentProjects *
+ide_application_get_recent_projects (IdeApplication *self)
+{
+ g_return_val_if_fail (IDE_IS_APPLICATION (self), NULL);
+
+ if (self->recent_projects == NULL)
+ {
+ self->recent_projects = ide_recent_projects_new ();
+ ide_recent_projects_discover_async (self->recent_projects, NULL, NULL, NULL);
+ }
+
+ return self->recent_projects;
+}
diff --git a/libide/ide-application.h b/libide/ide-application.h
new file mode 100644
index 0000000..ba2282c
--- /dev/null
+++ b/libide/ide-application.h
@@ -0,0 +1,57 @@
+/* ide-application.h
+ *
+ * Copyright (C) 2014 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/>.
+ */
+
+#ifndef IDE_APPLICATION_H
+#define IDE_APPLICATION_H
+
+#include <gtk/gtk.h>
+
+#include "ide-recent-projects.h"
+
+G_BEGIN_DECLS
+
+#define IDE_APPLICATION_DEFAULT (IDE_APPLICATION(g_application_get_default()))
+#define IDE_TYPE_APPLICATION (ide_application_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeApplication, ide_application, IDE, APPLICATION, GtkApplication)
+
+const gchar *ide_application_get_keybindings_mode (IdeApplication *self);
+GDateTime *ide_application_get_startup_time (IdeApplication *self);
+IdeRecentProjects *ide_application_get_recent_projects (IdeApplication *self);
+void ide_application_show_projects_window (IdeApplication *self);
+void ide_application_open_project_async (IdeApplication *self,
+ GFile *file,
+ GPtrArray *additional_files,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean ide_application_open_project_finish (IdeApplication *self,
+ GAsyncResult *result,
+ GError **error);
+void ide_application_get_worker_async (IdeApplication *self,
+ const gchar *plugin_name,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GDBusProxy *ide_application_get_worker_finish (IdeApplication *self,
+ GAsyncResult *result,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* IDE_APPLICATION_H */
diff --git a/libide/ide-css-provider.c b/libide/ide-css-provider.c
new file mode 100644
index 0000000..ccf1857
--- /dev/null
+++ b/libide/ide-css-provider.c
@@ -0,0 +1,176 @@
+/* ide-css-provider.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-css-provider"
+
+#include <glib/gi18n.h>
+#include <ide.h>
+
+#include "ide-css-provider.h"
+
+struct _IdeCssProvider
+{
+ GtkCssProvider parent_instance;
+
+ GtkSettings *settings;
+ gulong notify_gtk_theme_name_handler;
+};
+
+G_DEFINE_TYPE (IdeCssProvider, ide_css_provider, GTK_TYPE_CSS_PROVIDER)
+
+GtkCssProvider *
+ide_css_provider_new (void)
+{
+ return g_object_new (IDE_TYPE_CSS_PROVIDER, NULL);
+}
+
+static void
+ide_css_provider_update (IdeCssProvider *self)
+{
+ g_autofree gchar *theme_name = NULL;
+ g_autofree gchar *resource_path = NULL;
+ gboolean prefer_dark_theme = FALSE;
+ gsize len = 0;
+ guint32 flags = 0;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_CSS_PROVIDER (self));
+ g_assert (GTK_IS_SETTINGS (self->settings));
+
+ g_object_get (self->settings,
+ "gtk-theme-name", &theme_name,
+ "gtk-application-prefer-dark-theme", &prefer_dark_theme,
+ NULL);
+
+ resource_path = g_strdup_printf ("/org/gnome/builder/theme/%s%s.css",
+ theme_name,
+ prefer_dark_theme ? "-dark" : "");
+
+ if (!g_resources_get_info (resource_path, G_RESOURCE_LOOKUP_FLAGS_NONE, &len, &flags, NULL))
+ {
+ g_free (resource_path);
+ resource_path = g_strdup ("/org/gnome/builder/theme/shared.css");
+ }
+
+ gtk_css_provider_load_from_resource (GTK_CSS_PROVIDER (self), resource_path);
+
+ IDE_EXIT;
+}
+
+static void
+ide_css_provider__settings_notify_gtk_theme_name (IdeCssProvider *self,
+ GParamSpec *pspec,
+ GtkSettings *settings)
+{
+ g_assert (IDE_IS_CSS_PROVIDER (self));
+
+ ide_css_provider_update (self);
+}
+
+static void
+ide_css_provider__settings_notify_gtk_application_prefer_dark_theme (IdeCssProvider *self,
+ GParamSpec *pspec,
+ GtkSettings *settings)
+{
+ g_assert (IDE_IS_CSS_PROVIDER (self));
+
+ ide_css_provider_update (self);
+}
+
+static void
+ide_css_provider_parsing_error (GtkCssProvider *provider,
+ GtkCssSection *section,
+ const GError *error)
+{
+ g_autofree gchar *uri = NULL;
+ GFile *file;
+ guint line = 0;
+ guint line_offset = 0;
+
+ g_assert (IDE_IS_CSS_PROVIDER (provider));
+ g_assert (error != NULL);
+
+ if (section != NULL)
+ {
+ file = gtk_css_section_get_file (section);
+ uri = g_file_get_uri (file);
+ line = gtk_css_section_get_start_line (section);
+ line_offset = gtk_css_section_get_start_position (section);
+ g_warning ("Parsing Error: %s @ %u:%u: %s", uri, line, line_offset, error->message);
+ }
+ else
+ {
+ g_warning ("%s", error->message);
+ }
+}
+
+static void
+ide_css_provider_constructed (GObject *object)
+{
+ IdeCssProvider *self = (IdeCssProvider *)object;
+
+ G_OBJECT_CLASS (ide_css_provider_parent_class)->constructed (object);
+
+ self->settings = g_object_ref (gtk_settings_get_default ());
+
+ self->notify_gtk_theme_name_handler =
+ g_signal_connect_object (self->settings,
+ "notify::gtk-theme-name",
+ G_CALLBACK (ide_css_provider__settings_notify_gtk_theme_name),
+ self,
+ G_CONNECT_SWAPPED);
+
+ self->notify_gtk_theme_name_handler =
+ g_signal_connect_object (
+ self->settings,
+ "notify::gtk-application-prefer-dark-theme",
+ G_CALLBACK (ide_css_provider__settings_notify_gtk_application_prefer_dark_theme),
+ self,
+ G_CONNECT_SWAPPED);
+
+ ide_css_provider_update (self);
+}
+
+static void
+ide_css_provider_finalize (GObject *object)
+{
+ IdeCssProvider *self = (IdeCssProvider *)object;
+
+ ide_clear_signal_handler (self->settings, &self->notify_gtk_theme_name_handler);
+ g_clear_object (&self->settings);
+
+ G_OBJECT_CLASS (ide_css_provider_parent_class)->finalize (object);
+}
+
+static void
+ide_css_provider_class_init (IdeCssProviderClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkCssProviderClass *provider_class = GTK_CSS_PROVIDER_CLASS (klass);
+
+ object_class->finalize = ide_css_provider_finalize;
+ object_class->constructed = ide_css_provider_constructed;
+
+ provider_class->parsing_error = ide_css_provider_parsing_error;
+}
+
+static void
+ide_css_provider_init (IdeCssProvider *self)
+{
+}
diff --git a/src/greeter/gb-greeter-pill-box.h b/libide/ide-css-provider.h
similarity index 58%
copy from src/greeter/gb-greeter-pill-box.h
copy to libide/ide-css-provider.h
index 394a333..cd36e30 100644
--- a/src/greeter/gb-greeter-pill-box.h
+++ b/libide/ide-css-provider.h
@@ -1,4 +1,4 @@
-/* gb-greeter-pill-box.h
+/* ide-css-provider.h
*
* Copyright (C) 2015 Christian Hergert <christian hergert me>
*
@@ -16,22 +16,19 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef GB_GREETER_PILL_BOX_H
-#define GB_GREETER_PILL_BOX_H
+#ifndef IDE_CSS_PROVIDER_H
+#define IDE_CSS_PROVIDER_H
#include <gtk/gtk.h>
G_BEGIN_DECLS
-#define GB_TYPE_GREETER_PILL_BOX (gb_greeter_pill_box_get_type())
+#define IDE_TYPE_CSS_PROVIDER (ide_css_provider_get_type())
-G_DECLARE_FINAL_TYPE (GbGreeterPillBox, gb_greeter_pill_box, GB, GREETER_PILL_BOX, GtkEventBox)
+G_DECLARE_FINAL_TYPE (IdeCssProvider, ide_css_provider, IDE, CSS_PROVIDER, GtkCssProvider)
-GtkWidget *gb_greeter_pill_box_new (const gchar *label);
-const gchar *gb_greeter_pill_box_get_label (GbGreeterPillBox *self);
-void gb_greeter_pill_box_set_label (GbGreeterPillBox *self,
- const gchar *label);
+GtkCssProvider *ide_css_provider_new (void);
G_END_DECLS
-#endif /* GB_GREETER_PILL_BOX_H */
+#endif /* IDE_CSS_PROVIDER_H */
diff --git a/libide/ide-internal.h b/libide/ide-internal.h
index 814d8d9..460ac33 100644
--- a/libide/ide-internal.h
+++ b/libide/ide-internal.h
@@ -96,7 +96,7 @@ void _ide_source_view_set_count (IdeSourceView
guint count);
void _ide_source_view_set_modifier (IdeSourceView *self,
gunichar modifier);
-void _ide_thread_pool_init (void);
+void _ide_thread_pool_init (gboolean is_worker);
IdeUnsavedFile *_ide_unsaved_file_new (GFile *file,
GBytes *content,
const gchar *temp_path,
diff --git a/libide/ide-keybindings.c b/libide/ide-keybindings.c
new file mode 100644
index 0000000..37b2b32
--- /dev/null
+++ b/libide/ide-keybindings.c
@@ -0,0 +1,282 @@
+/* ide-keybindings.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-keybindings"
+
+#include <glib/gi18n.h>
+#include <ide.h>
+
+#include "ide-keybindings.h"
+
+struct _IdeKeybindings
+{
+ GObject parent_instance;
+
+ GtkApplication *application;
+ GtkCssProvider *css_provider;
+ gchar *mode;
+ guint constructed : 1;
+};
+
+enum
+{
+ PROP_0,
+ PROP_APPLICATION,
+ PROP_MODE,
+ LAST_PROP
+};
+
+G_DEFINE_TYPE (IdeKeybindings, ide_keybindings, G_TYPE_OBJECT)
+
+static GParamSpec *properties [LAST_PROP];
+
+IdeKeybindings *
+ide_keybindings_new (GtkApplication *application,
+ const gchar *mode)
+{
+ g_return_val_if_fail (GTK_IS_APPLICATION (application), NULL);
+
+ return g_object_new (IDE_TYPE_KEYBINDINGS,
+ "application", application,
+ "mode", mode,
+ NULL);
+}
+
+static void
+ide_keybindings_reload (IdeKeybindings *self)
+{
+ const gchar *mode;
+ g_autofree gchar *path = NULL;
+ g_autoptr(GBytes) bytes = NULL;
+ g_autoptr(GError) error = NULL;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_KEYBINDINGS (self));
+
+ mode = self->mode ? self->mode : "default";
+ IDE_TRACE_MSG ("Loading %s keybindings", mode);
+ path = g_strdup_printf ("/org/gnome/builder/keybindings/%s.css", mode);
+ bytes = g_resources_lookup_data (path, G_RESOURCE_LOOKUP_FLAGS_NONE, &error);
+
+ if (error == NULL)
+ gtk_css_provider_load_from_data (self->css_provider,
+ g_bytes_get_data (bytes, NULL),
+ g_bytes_get_size (bytes),
+ &error);
+
+ if (error)
+ g_warning ("%s", error->message);
+
+ IDE_EXIT;
+}
+
+const gchar *
+ide_keybindings_get_mode (IdeKeybindings *self)
+{
+ g_return_val_if_fail (IDE_IS_KEYBINDINGS (self), NULL);
+
+ return self->mode;
+}
+
+void
+ide_keybindings_set_mode (IdeKeybindings *self,
+ const gchar *mode)
+{
+ g_return_if_fail (IDE_IS_KEYBINDINGS (self));
+
+ if (mode != self->mode)
+ {
+ g_free (self->mode);
+ self->mode = g_strdup (mode);
+ if (self->constructed)
+ ide_keybindings_reload (self);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MODE]);
+ }
+}
+
+GtkApplication *
+ide_keybindings_get_application (IdeKeybindings *self)
+{
+ g_return_val_if_fail (IDE_IS_KEYBINDINGS (self), NULL);
+
+ return self->application;
+}
+
+static void
+ide_keybindings_set_application (IdeKeybindings *self,
+ GtkApplication *application)
+{
+ g_assert (IDE_IS_KEYBINDINGS (self));
+ g_assert (!application || GTK_IS_APPLICATION (application));
+
+ if (application != self->application)
+ {
+ if (self->application)
+ {
+ /* remove keybindings */
+ g_clear_object (&self->application);
+ }
+
+ if (application)
+ {
+ /* connect keybindings */
+ self->application = g_object_ref (application);
+ }
+ }
+}
+
+static void
+ide_keybindings_parsing_error (GtkCssProvider *css_provider,
+ GtkCssSection *section,
+ GError *error,
+ gpointer user_data)
+{
+ g_autofree gchar *filename = NULL;
+ GFile *file;
+ guint start_line;
+ guint end_line;
+
+ file = gtk_css_section_get_file (section);
+ filename = g_file_get_uri (file);
+ start_line = gtk_css_section_get_start_line (section);
+ end_line = gtk_css_section_get_end_line (section);
+
+ g_warning ("CSS parsing error in %s between lines %u and %u", filename, start_line, end_line);
+}
+
+static void
+ide_keybindings_constructed (GObject *object)
+{
+ IdeKeybindings *self = (IdeKeybindings *)object;
+ GdkScreen *screen;
+
+ IDE_ENTRY;
+
+ G_OBJECT_CLASS (ide_keybindings_parent_class)->constructed (object);
+
+ screen = gdk_screen_get_default ();
+ gtk_style_context_add_provider_for_screen (screen, GTK_STYLE_PROVIDER (self->css_provider),
+ GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+
+ self->constructed = TRUE;
+
+ ide_keybindings_reload (self);
+
+ IDE_EXIT;
+}
+
+static void
+ide_keybindings_finalize (GObject *object)
+{
+ IdeKeybindings *self = (IdeKeybindings *)object;
+
+ IDE_ENTRY;
+
+ g_clear_object (&self->application);
+ g_clear_object (&self->css_provider);
+ g_clear_pointer (&self->mode, g_free);
+
+ G_OBJECT_CLASS (ide_keybindings_parent_class)->finalize (object);
+
+ IDE_EXIT;
+}
+
+static void
+ide_keybindings_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdeKeybindings *self = IDE_KEYBINDINGS (object);
+
+ switch (prop_id)
+ {
+ case PROP_APPLICATION:
+ g_value_set_object (value, ide_keybindings_get_application (self));
+ break;
+
+ case PROP_MODE:
+ g_value_set_string (value, ide_keybindings_get_mode (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_keybindings_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdeKeybindings *self = IDE_KEYBINDINGS (object);
+
+ switch (prop_id)
+ {
+ case PROP_APPLICATION:
+ ide_keybindings_set_application (self, g_value_get_object (value));
+ break;
+
+ case PROP_MODE:
+ ide_keybindings_set_mode (self, g_value_get_string (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_keybindings_class_init (IdeKeybindingsClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = ide_keybindings_constructed;
+ object_class->finalize = ide_keybindings_finalize;
+ object_class->get_property = ide_keybindings_get_property;
+ object_class->set_property = ide_keybindings_set_property;
+
+ properties [PROP_APPLICATION] =
+ g_param_spec_object ("application",
+ "Application",
+ "The application to register keybindings for.",
+ GTK_TYPE_APPLICATION,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_MODE] =
+ g_param_spec_string ("mode",
+ "Mode",
+ "The name of the keybindings mode.",
+ NULL,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, LAST_PROP, properties);
+}
+
+static void
+ide_keybindings_init (IdeKeybindings *self)
+{
+ self->css_provider = gtk_css_provider_new ();
+
+ g_signal_connect (self->css_provider,
+ "parsing-error",
+ G_CALLBACK (ide_keybindings_parsing_error),
+ NULL);
+}
diff --git a/libide/ide-keybindings.h b/libide/ide-keybindings.h
new file mode 100644
index 0000000..a1196b6
--- /dev/null
+++ b/libide/ide-keybindings.h
@@ -0,0 +1,39 @@
+/* ide-keybindings.h
+ *
+ * Copyright (C) 2014-2015 Christian Hergert <christian hergert me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_KEYBINDINGS_H
+#define IDE_KEYBINDINGS_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_KEYBINDINGS (ide_keybindings_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeKeybindings, ide_keybindings, IDE, KEYBINDINGS, GObject)
+
+IdeKeybindings *ide_keybindings_new (GtkApplication *application,
+ const gchar *mode);
+GtkApplication *ide_keybindings_get_application (IdeKeybindings *self);
+const gchar *ide_keybindings_get_mode (IdeKeybindings *self);
+void ide_keybindings_set_mode (IdeKeybindings *self,
+ const gchar *name);
+
+G_END_DECLS
+
+#endif /* IDE_KEYBINDINGS_H */
diff --git a/libide/ide-layout-manager.c b/libide/ide-layout-manager.c
new file mode 100644
index 0000000..ff37280
--- /dev/null
+++ b/libide/ide-layout-manager.c
@@ -0,0 +1,72 @@
+/* ide-layout-manager.c
+ *
+ * Copyright (C) 2015 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ide-layout-manager.h"
+
+G_DEFINE_INTERFACE (IdeLayoutManager, ide_layout_manager, G_TYPE_OBJECT)
+
+static void
+ide_layout_manager_default_init (IdeLayoutManagerInterface *iface)
+{
+}
+
+/**
+ * ide_layout_manager_add:
+ * @self: An #IdeLayoutManager.
+ * @hints: (nullable): optional hints for the layout.
+ * @child: A #GtkWidget.
+ *
+ * Adds @child to the layout, optionally hinting to the layout manager about where to
+ * place the child.
+ *
+ * Returns: A layout specific identifier greater than zero, which should be passed to
+ * ide_layout_manager_remove() to remove the widget from the layout.
+ */
+guint
+ide_layout_manager_add (IdeLayoutManager *self,
+ const IdeLayoutHints *hints,
+ GtkWidget *child)
+{
+ guint ret;
+
+ g_return_val_if_fail (IDE_IS_LAYOUT_MANAGER (self), 0);
+ g_return_val_if_fail (GTK_IS_WIDGET (child), 0);
+
+ ret = IDE_LAYOUT_MANAGER_GET_IFACE (self)->add (self, hints, child);
+
+ g_return_val_if_fail (ret > 0, 0);
+
+ return ret;
+}
+
+/**
+ * ide_layout_manager_remove:
+ * @self: An #IdeLayoutManager.
+ * @layout_id: The layout id from ide_layout_manager_add()
+ *
+ * Removes a widget from the layout that was generated by calling * ide_layout_manager_add().
+ */
+void
+ide_layout_manager_remove (IdeLayoutManager *self,
+ guint layout_id)
+{
+ g_return_if_fail (IDE_IS_LAYOUT_MANAGER (self));
+ g_return_if_fail (layout_id > 0);
+
+ IDE_LAYOUT_MANAGER_GET_IFACE (self)->remove (self, layout_id);
+}
diff --git a/libide/ide-layout-manager.h b/libide/ide-layout-manager.h
new file mode 100644
index 0000000..3372138
--- /dev/null
+++ b/libide/ide-layout-manager.h
@@ -0,0 +1,60 @@
+/* ide-layout-manager.h
+ *
+ * Copyright (C) 2015 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/>.
+ */
+
+#ifndef IDE_LAYOUT_MANAGER_H
+#define IDE_LAYOUT_MANAGER_H
+
+#include <gtk/gtk.h>
+
+#include "ide-view.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_LAYOUT_MANAGER (ide_layout_manager_get_type())
+
+G_DECLARE_INTERFACE (IdeLayoutManager, ide_layout_manager, IDE, LAYOUT_MANAGER, GObject)
+
+typedef struct
+{
+ GtkWidget *left_of;
+ GtkWidget *right_of;
+ GtkWidget *above;
+ GtkWidget *below;
+ gint column;
+} IdeLayoutHints;
+
+struct _IdeLayoutManagerInterface
+{
+ GTypeInterface parent_instance;
+
+ guint (*add) (IdeLayoutManager *self,
+ const IdeLayoutHints *hints,
+ GtkWidget *child);
+ void (*remove) (IdeLayoutManager *self,
+ guint layout_id);
+};
+
+guint ide_layout_manager_add (IdeLayoutManager *self,
+ const IdeLayoutHints *hints,
+ GtkWidget *child);
+void ide_layout_manager_remove (IdeLayoutManager *self,
+ guint layout_id);
+
+G_END_DECLS
+
+#endif /* IDE_LAYOUT_MANAGER_H */
diff --git a/libide/ide-perspective.c b/libide/ide-perspective.c
new file mode 100644
index 0000000..3a322b7
--- /dev/null
+++ b/libide/ide-perspective.c
@@ -0,0 +1,263 @@
+/* ide-perspective.c
+ *
+ * Copyright (C) 2015 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ide-perspective.h"
+
+G_DEFINE_INTERFACE (IdePerspective, ide_perspective, G_TYPE_OBJECT)
+
+static gboolean
+ide_perspective_real_agree_to_shutdown (IdePerspective *self)
+{
+ return TRUE;
+}
+
+static GActionGroup *
+ide_perspective_real_get_actions (IdePerspective *self)
+{
+ return NULL;
+}
+
+static const gchar *
+ide_perspective_real_get_icon_name (IdePerspective *self)
+{
+ return NULL;
+}
+
+static const gchar *
+ide_perspective_real_get_id (IdePerspective *self)
+{
+ return G_OBJECT_TYPE_NAME (self);
+}
+
+static gboolean
+ide_perspective_real_get_needs_attention (IdePerspective *self)
+{
+ return FALSE;
+}
+
+static const gchar *
+ide_perspective_real_get_title (IdePerspective *self)
+{
+ return NULL;
+}
+
+static GtkWidget *
+ide_perspective_real_get_titlebar (IdePerspective *self)
+{
+ return NULL;
+}
+
+static void
+ide_perspective_real_set_fullscreen (IdePerspective *self,
+ gboolean fullscreen)
+{
+}
+
+static void
+ide_perspective_real_views_foreach (IdePerspective *self,
+ GtkCallback callback,
+ gpointer user_data)
+{
+}
+
+static void
+ide_perspective_default_init (IdePerspectiveInterface *iface)
+{
+ iface->agree_to_shutdown = ide_perspective_real_agree_to_shutdown;
+ iface->get_actions = ide_perspective_real_get_actions;
+ iface->get_icon_name = ide_perspective_real_get_icon_name;
+ iface->get_id = ide_perspective_real_get_id;
+ iface->get_needs_attention = ide_perspective_real_get_needs_attention;
+ iface->get_title = ide_perspective_real_get_title;
+ iface->get_titlebar = ide_perspective_real_get_titlebar;
+ iface->set_fullscreen = ide_perspective_real_set_fullscreen;
+ iface->views_foreach = ide_perspective_real_views_foreach;
+}
+
+/**
+ * ide_perspective_agree_to_shutdown:
+ * @self: An #IdePerspective.
+ *
+ * This interface method is called when the workbench would like to shutdown.
+ * If the perspective needs to focus and ask the user a question, this is the place
+ * to do so. You may run a #GtkDialog using gtk_dialog_run() or simply focus your
+ * perspective and return %FALSE.
+ *
+ * Returns: %TRUE to allow the workbench to continue shutting down.
+ */
+gboolean
+ide_perspective_agree_to_shutdown (IdePerspective *self)
+{
+ g_return_val_if_fail (IDE_IS_PERSPECTIVE (self), FALSE);
+
+ return IDE_PERSPECTIVE_GET_IFACE (self)->agree_to_shutdown (self);
+}
+
+/**
+ * ide_perspective_get_icon_name:
+ * @self: An #IdePerspective.
+ *
+ * This interface methods retrieves the icon name to use when displaying the
+ * perspective selection sidebar.
+ *
+ * If you implement an "icon-name" property, the icon may change at runtime.
+ *
+ * Returns: A named icon as a string which will not be modified or freed.
+ */
+const gchar *
+ide_perspective_get_icon_name (IdePerspective *self)
+{
+ g_return_val_if_fail (IDE_IS_PERSPECTIVE (self), NULL);
+
+ return IDE_PERSPECTIVE_GET_IFACE (self)->get_icon_name (self);
+}
+
+/**
+ * ide_perspective_get_id:
+ * @self: An #IdePerspective
+ *
+ * This interface method is used to identify the perspective. It should be a short
+ * internal name, such as "editor" which should not be translated. Internally, the
+ * default implementation of this method will return the name of the instances #GType.
+ *
+ * This value should be unique per workspace.
+ *
+ * Returns: A string identifier for the perspective.
+ */
+const gchar *
+ide_perspective_get_id (IdePerspective *self)
+{
+ g_return_val_if_fail (IDE_IS_PERSPECTIVE (self), NULL);
+
+ return IDE_PERSPECTIVE_GET_IFACE (self)->get_id (self);
+}
+
+/**
+ * ide_perspective_get_needs_attention:
+ * @self: An #IdePerspective.
+ *
+ * This interface method returns %TRUE if the interface needs attention.
+ *
+ * One such use of this would be to indicate that contents within a perspective have
+ * changed since the user last focused the perspective. This should also be implemented
+ * with a boolean property named "needs-attention". If you call g_object_notify() (or one
+ * of its variants), the notifcation visual will be rendered with your icon.
+ *
+ * Returns: %TRUE if the perspective needs attention.
+ */
+gboolean
+ide_perspective_get_needs_attention (IdePerspective *self)
+{
+ g_return_val_if_fail (IDE_IS_PERSPECTIVE (self), FALSE);
+
+ return IDE_PERSPECTIVE_GET_IFACE (self)->get_needs_attention (self);
+}
+
+/**
+ * ide_perspective_get_title:
+ * @self: An #IdePerspective
+ *
+ * This interface method gets the title of the perspective. This is used for tooltips
+ * in the perspective selector and potentially other UI components.
+ *
+ * Returns: A string which will not be modified or freed.
+ */
+const gchar *
+ide_perspective_get_title (IdePerspective *self)
+{
+ g_return_val_if_fail (IDE_IS_PERSPECTIVE (self), NULL);
+
+ return IDE_PERSPECTIVE_GET_IFACE (self)->get_title (self);
+}
+
+/**
+ * ide_perspective_get_titlebar:
+ * @self: An #IdePerspective.
+ *
+ * This interface method should return a #GtkWidget suitable for being embedded as the
+ * titlebar for the application. If you return %NULL from this method, a suitable titlebar
+ * will be created for you.
+ *
+ * You may use #IdeHeaderBar for a base implementation to save you the trouble of
+ * creating a titlebar similar to other perspectives in Builder.
+ *
+ * Returns: (transfer none) (nullable): A #GtkWidget or %NULL.
+ */
+GtkWidget *
+ide_perspective_get_titlebar (IdePerspective *self)
+{
+ g_return_val_if_fail (IDE_IS_PERSPECTIVE (self), NULL);
+
+ return IDE_PERSPECTIVE_GET_IFACE (self)->get_titlebar (self);
+}
+
+/**
+ * ide_perspective_set_fullscreen:
+ * @self: An #IdePerspective.
+ * @fullscreen: If fullscreen mode should be activated.
+ *
+ * This interface method is used to notify the perspective that it is going into
+ * fullscreen mode. The #IdeWorkbench will notify the perspective before it is displayed.
+ */
+void
+ide_perspective_set_fullscreen (IdePerspective *self,
+ gboolean fullscreen)
+{
+ g_return_if_fail (IDE_IS_PERSPECTIVE (self));
+
+ IDE_PERSPECTIVE_GET_IFACE (self)->set_fullscreen (self, fullscreen);
+}
+
+/**
+ * ide_perspective_views_foreach:
+ * @self: An #IdePerspective.
+ * @callback: (scope call): A #GtkCallback.
+ * @user_data: user data for @callback.
+ *
+ * This interface method is used to iterate all #IdeView's that are descendents of @self.
+ */
+void
+ide_perspective_views_foreach (IdePerspective *self,
+ GtkCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (IDE_IS_PERSPECTIVE (self));
+ g_return_if_fail (callback != NULL);
+
+ IDE_PERSPECTIVE_GET_IFACE (self)->views_foreach (self, callback, user_data);
+}
+
+/**
+ * ide_perspective_get_actions:
+ * @self: An #IdePerspective.
+ *
+ * This interface method should retrieve a #GActionGroup associated with the
+ * perspective, if necessary. The #GActionGroup will automatically be
+ * registered with the "perspective" action prefix while the perspective is
+ * active. A perspective is "active" when it is currently displayed in the
+ * workbench.
+ *
+ * Returns: (nullable) (transfer full): A #GActionGroup or %NULL.
+ */
+GActionGroup *
+ide_perspective_get_actions (IdePerspective *self)
+{
+ g_return_val_if_fail (IDE_IS_PERSPECTIVE (self), NULL);
+
+ return IDE_PERSPECTIVE_GET_IFACE (self)->get_actions (self);
+}
diff --git a/libide/ide-perspective.h b/libide/ide-perspective.h
new file mode 100644
index 0000000..9563d84
--- /dev/null
+++ b/libide/ide-perspective.h
@@ -0,0 +1,63 @@
+/* ide-perspective.h
+ *
+ * Copyright (C) 2015 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/>.
+ */
+
+#ifndef IDE_PERSPECTIVE_H
+#define IDE_PERSPECTIVE_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_PERSPECTIVE (ide_perspective_get_type())
+
+G_DECLARE_INTERFACE (IdePerspective, ide_perspective, IDE, PERSPECTIVE, GtkWidget)
+
+struct _IdePerspectiveInterface
+{
+ GTypeInterface parent;
+
+ gboolean (*agree_to_shutdown) (IdePerspective *self);
+ GActionGroup *(*get_actions) (IdePerspective *self);
+ const gchar *(*get_icon_name) (IdePerspective *self);
+ const gchar *(*get_id) (IdePerspective *self);
+ gboolean (*get_needs_attention) (IdePerspective *self);
+ const gchar *(*get_title) (IdePerspective *self);
+ GtkWidget *(*get_titlebar) (IdePerspective *self);
+ void (*set_fullscreen) (IdePerspective *self,
+ gboolean fullscreen);
+ void (*views_foreach) (IdePerspective *self,
+ GtkCallback callback,
+ gpointer user_data);
+};
+
+gboolean ide_perspective_agree_to_shutdown (IdePerspective *self);
+GActionGroup *ide_perspective_get_actions (IdePerspective *self);
+const gchar *ide_perspective_get_icon_name (IdePerspective *self);
+const gchar *ide_perspective_get_id (IdePerspective *self);
+const gchar *ide_perspective_get_title (IdePerspective *self);
+GtkWidget *ide_perspective_get_titlebar (IdePerspective *self);
+gboolean ide_perspective_get_needs_attention (IdePerspective *self);
+void ide_perspective_set_fullscreen (IdePerspective *self,
+ gboolean fullscreen);
+void ide_perspective_views_foreach (IdePerspective *self,
+ GtkCallback callback,
+ gpointer user_data);
+
+G_END_DECLS
+
+#endif /* IDE_PERSPECTIVE_H */
diff --git a/libide/ide-preferences-addin.c b/libide/ide-preferences-addin.c
new file mode 100644
index 0000000..2463942
--- /dev/null
+++ b/libide/ide-preferences-addin.c
@@ -0,0 +1,81 @@
+/* ide-preferences-addin.c
+ *
+ * Copyright (C) 2015 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ide-preferences-addin.h"
+
+G_DEFINE_INTERFACE (IdePreferencesAddin, ide_preferences_addin, G_TYPE_OBJECT)
+
+static void
+ide_preferences_addin_real_load (IdePreferencesAddin *self,
+ IdePreferences *preferences)
+{
+}
+
+static void
+ide_preferences_addin_real_unload (IdePreferencesAddin *self,
+ IdePreferences *preferences)
+{
+}
+
+static void
+ide_preferences_addin_default_init (IdePreferencesAddinInterface *iface)
+{
+ iface->load = ide_preferences_addin_real_load;
+ iface->unload = ide_preferences_addin_real_unload;
+}
+
+/**
+ * ide_preferences_addin_load:
+ * @self: An #IdePreferencesAddin.
+ * @preferences: The preferences container implementation.
+ *
+ * This interface method is called when a preferences addin is initialized. It could be
+ * initialized from multiple preferences implementations, so consumers should use the
+ * #IdePreferences interface to add their preferences controls to the container.
+ *
+ * Such implementations might include a preferences dialog window, or a preferences
+ * widget which could be rendered as a perspective.
+ */
+void
+ide_preferences_addin_load (IdePreferencesAddin *self,
+ IdePreferences *preferences)
+{
+ g_return_if_fail (IDE_IS_PREFERENCES_ADDIN (self));
+ g_return_if_fail (IDE_IS_PREFERENCES (preferences));
+
+ IDE_PREFERENCES_ADDIN_GET_IFACE (self)->load (self, preferences);
+}
+
+/**
+ * ide_preferences_addin_unload:
+ * @self: An #IdePreferencesAddin.
+ * @preferences: The preferences container implementation.
+ *
+ * This interface method is called when the preferences addin should remove all controls
+ * added to @preferences. This could happen during desctruction of @preferences, or when
+ * the plugin is unloaded.
+ */
+void
+ide_preferences_addin_unload (IdePreferencesAddin *self,
+ IdePreferences *preferences)
+{
+ g_return_if_fail (IDE_IS_PREFERENCES_ADDIN (self));
+ g_return_if_fail (IDE_IS_PREFERENCES (preferences));
+
+ IDE_PREFERENCES_ADDIN_GET_IFACE (self)->unload (self, preferences);
+}
diff --git a/libide/ide-preferences-addin.h b/libide/ide-preferences-addin.h
new file mode 100644
index 0000000..308b7be
--- /dev/null
+++ b/libide/ide-preferences-addin.h
@@ -0,0 +1,49 @@
+/* ide-preferences-addin.h
+ *
+ * Copyright (C) 2015 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/>.
+ */
+
+#ifndef IDE_PREFERENCES_ADDIN_H
+#define IDE_PREFERENCES_ADDIN_H
+
+#include <gtk/gtk.h>
+
+#include "ide-preferences.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_PREFERENCES_ADDIN (ide_preferences_addin_get_type())
+
+G_DECLARE_INTERFACE (IdePreferencesAddin, ide_preferences_addin, IDE, PREFERENCES_ADDIN, GObject)
+
+struct _IdePreferencesAddinInterface
+{
+ GTypeInterface parent_interface;
+
+ void (*load) (IdePreferencesAddin *self,
+ IdePreferences *preferences);
+ void (*unload) (IdePreferencesAddin *self,
+ IdePreferences *preferences);
+};
+
+void ide_preferences_addin_load (IdePreferencesAddin *self,
+ IdePreferences *preferences);
+void ide_preferences_addin_unload (IdePreferencesAddin *self,
+ IdePreferences *preferences);
+
+G_END_DECLS
+
+#endif /* IDE_PREFERENCES_ADDIN_H */
diff --git a/libide/ide-preferences.c b/libide/ide-preferences.c
new file mode 100644
index 0000000..eaae59d
--- /dev/null
+++ b/libide/ide-preferences.c
@@ -0,0 +1,116 @@
+/* ide-preferences.c
+ *
+ * Copyright (C) 2015 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ide-preferences.h"
+
+G_DEFINE_INTERFACE (IdePreferences, ide_preferences, G_TYPE_OBJECT)
+
+static void
+ide_preferences_default_init (IdePreferencesInterface *iface)
+{
+}
+
+void
+ide_preferences_add_page (IdePreferences *self,
+ const gchar *page_name,
+ const gchar *title,
+ gint priority)
+{
+ g_return_if_fail (IDE_IS_PREFERENCES (self));
+ g_return_if_fail (page_name != NULL);
+ g_return_if_fail (title != NULL);
+
+ IDE_PREFERENCES_GET_IFACE (self)->add_page (self, page_name, title, priority);
+}
+
+void
+ide_preferences_add_group (IdePreferences *self,
+ const gchar *page_name,
+ const gchar *group_name,
+ const gchar *title,
+ gint priority)
+{
+ g_return_if_fail (IDE_IS_PREFERENCES (self));
+ g_return_if_fail (page_name != NULL);
+ g_return_if_fail (group_name != NULL);
+ g_return_if_fail (title != NULL);
+
+ IDE_PREFERENCES_GET_IFACE (self)->add_group (self, page_name, group_name, title, priority);
+}
+
+guint
+ide_preferences_add_switch (IdePreferences *self,
+ const gchar *page_name,
+ const gchar *group_name,
+ const gchar *schema_id,
+ const gchar *key,
+ const gchar *title,
+ const gchar *subtitle,
+ const gchar *keywords,
+ gint priority)
+{
+ g_return_val_if_fail (IDE_IS_PREFERENCES (self), 0);
+ g_return_val_if_fail (page_name != NULL, 0);
+ g_return_val_if_fail (group_name != NULL, 0);
+ g_return_val_if_fail (schema_id != NULL, 0);
+ g_return_val_if_fail (key != NULL, 0);
+ g_return_val_if_fail (title != NULL, 0);
+
+ return IDE_PREFERENCES_GET_IFACE (self)->add_switch (self, page_name, group_name, schema_id, key,
+ title, subtitle, keywords, priority);
+}
+
+guint
+ide_preferences_add_spinbutton (IdePreferences *self,
+ const gchar *page_name,
+ const gchar *group_name,
+ const gchar *schema_id,
+ const gchar *key,
+ const gchar *title,
+ const gchar *subtitle,
+ const gchar *keywords,
+ gint priority)
+{
+ g_return_val_if_fail (IDE_IS_PREFERENCES (self), 0);
+ g_return_val_if_fail (page_name != NULL, 0);
+ g_return_val_if_fail (group_name != NULL, 0);
+ g_return_val_if_fail (schema_id != NULL, 0);
+ g_return_val_if_fail (key != NULL, 0);
+ g_return_val_if_fail (title != NULL, 0);
+
+ return IDE_PREFERENCES_GET_IFACE (self)->add_spinbutton (self, page_name, group_name, schema_id,
+ key, title, subtitle, keywords,
+ priority);
+}
+
+guint
+ide_preferences_add_custom (IdePreferences *self,
+ const gchar *page_name,
+ const gchar *group_name,
+ GtkWidget *widget,
+ const gchar *keywords,
+ gint priority)
+{
+ g_return_val_if_fail (IDE_IS_PREFERENCES (self), 0);
+ g_return_val_if_fail (page_name != NULL, 0);
+ g_return_val_if_fail (group_name != NULL, 0);
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
+
+ return IDE_PREFERENCES_GET_IFACE (self)->add_custom (self, page_name, group_name, widget,
+ keywords, priority);
+}
diff --git a/libide/ide-preferences.h b/libide/ide-preferences.h
new file mode 100644
index 0000000..bca3d13
--- /dev/null
+++ b/libide/ide-preferences.h
@@ -0,0 +1,105 @@
+/* ide-preferences.h
+ *
+ * Copyright (C) 2015 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/>.
+ */
+
+#ifndef IDE_PREFERENCES_H
+#define IDE_PREFERENCES_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_PREFERENCES (ide_preferences_get_type())
+
+G_DECLARE_INTERFACE (IdePreferences, ide_preferences, IDE, PREFERENCES, GObject)
+
+struct _IdePreferencesInterface
+{
+ GTypeInterface parent_interface;
+
+ void (*add_page) (IdePreferences *self,
+ const gchar *page_name,
+ const gchar *title,
+ gint priority);
+ void (*add_group) (IdePreferences *self,
+ const gchar *page_name,
+ const gchar *group_name,
+ const gchar *title,
+ gint priority);
+ guint (*add_switch) (IdePreferences *self,
+ const gchar *page_name,
+ const gchar *group_name,
+ const gchar *schema_id,
+ const gchar *key,
+ const gchar *title,
+ const gchar *subtitle,
+ const gchar *keywords,
+ gint priority);
+ guint (*add_spinbutton) (IdePreferences *self,
+ const gchar *page_name,
+ const gchar *group_name,
+ const gchar *schema_id,
+ const gchar *key,
+ const gchar *title,
+ const gchar *subtitle,
+ const gchar *keywords,
+ gint priority);
+ guint (*add_custom) (IdePreferences *self,
+ const gchar *page_name,
+ const gchar *group_name,
+ GtkWidget *widget,
+ const gchar *keywords,
+ gint priority);
+};
+
+void ide_preferences_add_page (IdePreferences *self,
+ const gchar *page_name,
+ const gchar *title,
+ gint priority);
+void ide_preferences_add_group (IdePreferences *self,
+ const gchar *page_name,
+ const gchar *group_name,
+ const gchar *title,
+ gint priority);
+guint ide_preferences_add_switch (IdePreferences *self,
+ const gchar *page_name,
+ const gchar *group_name,
+ const gchar *schema_id,
+ const gchar *key,
+ const gchar *title,
+ const gchar *subtitle,
+ const gchar *keywords,
+ gint priority);
+guint ide_preferences_add_spinbutton (IdePreferences *self,
+ const gchar *page_name,
+ const gchar *group_name,
+ const gchar *schema_id,
+ const gchar *key,
+ const gchar *title,
+ const gchar *subtitle,
+ const gchar *keywords,
+ gint priority);
+guint ide_preferences_add_custom (IdePreferences *self,
+ const gchar *page_name,
+ const gchar *group_name,
+ GtkWidget *widget,
+ const gchar *keywords,
+ gint priority);
+
+G_END_DECLS
+
+#endif /* IDE_PREFERENCES_H */
diff --git a/libide/ide-thread-pool.c b/libide/ide-thread-pool.c
index d45c830..4eaa613 100644
--- a/libide/ide-thread-pool.c
+++ b/libide/ide-thread-pool.c
@@ -183,8 +183,19 @@ ide_thread_pool_worker (gpointer data,
}
void
-_ide_thread_pool_init (void)
+_ide_thread_pool_init (gboolean is_worker)
{
+ gint compiler = COMPILER_MAX_THREADS;
+ gint indexer = INDEXER_MAX_THREADS;
+ gboolean shared = FALSE;
+
+ if (is_worker)
+ {
+ compiler = 1;
+ indexer = 1;
+ shared = TRUE;
+ }
+
/*
* Create our thread pool exclusive to compiler tasks (such as those from Clang).
* We don't want to consume threads fro other GTask's such as those regarding IO so we manage
@@ -192,8 +203,8 @@ _ide_thread_pool_init (void)
*/
thread_pools [IDE_THREAD_POOL_COMPILER] = g_thread_pool_new (ide_thread_pool_worker,
NULL,
- COMPILER_MAX_THREADS,
- FALSE,
+ compiler,
+ shared,
NULL);
/*
@@ -202,7 +213,7 @@ _ide_thread_pool_init (void)
*/
thread_pools [IDE_THREAD_POOL_INDEXER] = g_thread_pool_new (ide_thread_pool_worker,
NULL,
- INDEXER_MAX_THREADS,
- FALSE,
+ indexer,
+ shared,
NULL);
}
diff --git a/libide/ide-view.c b/libide/ide-view.c
new file mode 100644
index 0000000..6a7ad9c
--- /dev/null
+++ b/libide/ide-view.c
@@ -0,0 +1,129 @@
+/* ide-view.c
+ *
+ * Copyright (C) 2015 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ide-view.h"
+
+G_DEFINE_INTERFACE (IdeView, ide_view, GTK_TYPE_WIDGET)
+
+static const gchar *
+ide_view_real_get_title (IdeView *self)
+{
+ return NULL;
+}
+
+static const gchar *
+ide_view_real_get_icon_name (IdeView *self)
+{
+ return NULL;
+}
+
+static gboolean
+ide_view_real_get_can_save (IdeView *self)
+{
+ return FALSE;
+}
+
+static gboolean
+ide_view_real_get_needs_attention (IdeView *self)
+{
+ return FALSE;
+}
+
+static void
+ide_view_real_save_async (IdeView *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_task_report_new_error (self, callback, user_data, ide_view_real_save_async,
+ G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Saving is not supported.");
+}
+
+static gboolean
+ide_view_real_save_finish (IdeView *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+ide_view_default_init (IdeViewInterface *iface)
+{
+ iface->get_title = ide_view_real_get_title;
+ iface->get_icon_name = ide_view_real_get_icon_name;
+ iface->get_can_save = ide_view_real_get_can_save;
+ iface->get_needs_attention = ide_view_real_get_needs_attention;
+ iface->save_async = ide_view_real_save_async;
+ iface->save_finish = ide_view_real_save_finish;
+}
+
+const gchar *
+ide_view_get_title (IdeView *self)
+{
+ g_return_val_if_fail (IDE_IS_VIEW (self), NULL);
+
+ return NULL;
+}
+
+const gchar *
+ide_view_get_icon_name (IdeView *self)
+{
+ g_return_val_if_fail (IDE_IS_VIEW (self), NULL);
+
+ return IDE_VIEW_GET_IFACE (self)->get_icon_name (self);
+}
+
+gboolean
+ide_view_get_can_save (IdeView *self)
+{
+ g_return_val_if_fail (IDE_IS_VIEW (self), FALSE);
+
+ return IDE_VIEW_GET_IFACE (self)->get_can_save (self);
+}
+
+gboolean
+ide_view_get_needs_attention (IdeView *self)
+{
+ g_return_val_if_fail (IDE_IS_VIEW (self), FALSE);
+
+ return IDE_VIEW_GET_IFACE (self)->get_needs_attention (self);
+}
+
+void
+ide_view_save_async (IdeView *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (IDE_IS_VIEW (self));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ IDE_VIEW_GET_IFACE (self)->save_async (self, cancellable, callback, user_data);
+}
+
+gboolean
+ide_view_save_finish (IdeView *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (IDE_IS_VIEW (self), FALSE);
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+ return IDE_VIEW_GET_IFACE (self)->save_finish (self, result, error);
+}
diff --git a/libide/ide-view.h b/libide/ide-view.h
new file mode 100644
index 0000000..d73f462
--- /dev/null
+++ b/libide/ide-view.h
@@ -0,0 +1,61 @@
+/* ide-view.h
+ *
+ * Copyright (C) 2015 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/>.
+ */
+
+#ifndef IDE_VIEW_H
+#define IDE_VIEW_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_VIEW (ide_view_get_type())
+
+G_DECLARE_INTERFACE (IdeView, ide_view, IDE, VIEW, GtkWidget)
+
+struct _IdeViewInterface
+{
+ GTypeInterface parent;
+
+ const gchar *(*get_title) (IdeView *self);
+ const gchar *(*get_icon_name) (IdeView *self);
+ gboolean (*get_can_save) (IdeView *self);
+ gboolean (*get_needs_attention) (IdeView *self);
+ void (*save_async) (IdeView *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (*save_finish) (IdeView *self,
+ GAsyncResult *result,
+ GError **error);
+};
+
+const gchar *ide_view_get_title (IdeView *self);
+const gchar *ide_view_get_icon_name (IdeView *self);
+gboolean ide_view_get_can_save (IdeView *self);
+gboolean ide_view_get_needs_attention (IdeView *self);
+void ide_view_save_async (IdeView *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean ide_view_save_finish (IdeView *self,
+ GAsyncResult *result,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* IDE_VIEW_H */
diff --git a/libide/ide-workbench-addin.c b/libide/ide-workbench-addin.c
new file mode 100644
index 0000000..1cf1d61
--- /dev/null
+++ b/libide/ide-workbench-addin.c
@@ -0,0 +1,83 @@
+/* ide-workbench-addin.c
+ *
+ * Copyright (C) 2015 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-workbench-addin"
+
+#include "ide-workbench-addin.h"
+
+G_DEFINE_INTERFACE (IdeWorkbenchAddin, ide_workbench_addin, G_TYPE_OBJECT)
+
+static void
+ide_workbench_addin_real_load (IdeWorkbenchAddin *self,
+ IdeWorkbench *workbench)
+{
+}
+
+static void
+ide_workbench_addin_real_unload (IdeWorkbenchAddin *self,
+ IdeWorkbench *workbench)
+{
+}
+
+static void
+ide_workbench_addin_default_init (IdeWorkbenchAddinInterface *iface)
+{
+ iface->load = ide_workbench_addin_real_load;
+ iface->unload = ide_workbench_addin_real_unload;
+}
+
+/**
+ * ide_workbench_addin_load:
+ * @self: An #IdeWorkbenchAddin
+ * @workbench: An #IdeWorkbench
+ *
+ * This interface method is called to load @self. Addin implementations should add any
+ * required UI or actions to @workbench here. You should remove anything you've added
+ * in ide_workbench_addin_unload(), as that will be called when your plugin is deactivated
+ * or the workbench is in the destruction process.
+ */
+void
+ide_workbench_addin_load (IdeWorkbenchAddin *self,
+ IdeWorkbench *workbench)
+{
+ g_return_if_fail (IDE_IS_WORKBENCH_ADDIN (self));
+ g_return_if_fail (IDE_IS_WORKBENCH (workbench));
+
+ IDE_WORKBENCH_ADDIN_GET_IFACE (self)->load (self, workbench);
+}
+
+/**
+ * ide_workbench_addin_unload:
+ * @self: An #IdeWorkbenchAddin
+ * @workbench: An #IdeWorkbench
+ *
+ * This interface method should cleanup after anything added to @workbench in
+ * ide_workbench_addin_load().
+ *
+ * This might be called when a plugin is deactivated, or the workbench is in the
+ * destruction process.
+ */
+void
+ide_workbench_addin_unload (IdeWorkbenchAddin *self,
+ IdeWorkbench *workbench)
+{
+ g_return_if_fail (IDE_IS_WORKBENCH_ADDIN (self));
+ g_return_if_fail (IDE_IS_WORKBENCH (workbench));
+
+ IDE_WORKBENCH_ADDIN_GET_IFACE (self)->unload (self, workbench);
+}
diff --git a/libide/ide-workbench-addin.h b/libide/ide-workbench-addin.h
new file mode 100644
index 0000000..9897d2b
--- /dev/null
+++ b/libide/ide-workbench-addin.h
@@ -0,0 +1,47 @@
+/* ide-workbench-addin.h
+ *
+ * Copyright (C) 2015 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/>.
+ */
+
+#ifndef IDE_WORKBENCH_ADDIN_H
+#define IDE_WORKBENCH_ADDIN_H
+
+#include "ide-workbench.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_WORKBENCH_ADDIN (ide_workbench_addin_get_type())
+
+G_DECLARE_INTERFACE (IdeWorkbenchAddin, ide_workbench_addin, IDE, WORKBENCH_ADDIN, GObject)
+
+struct _IdeWorkbenchAddinInterface
+{
+ GTypeInterface parent;
+
+ void (*load) (IdeWorkbenchAddin *self,
+ IdeWorkbench *workbench);
+ void (*unload) (IdeWorkbenchAddin *self,
+ IdeWorkbench *workbench);
+};
+
+void ide_workbench_addin_load (IdeWorkbenchAddin *self,
+ IdeWorkbench *workbench);
+void ide_workbench_addin_unload (IdeWorkbenchAddin *self,
+ IdeWorkbench *workbench);
+
+G_END_DECLS
+
+#endif /* IDE_WORKBENCH_ADDIN_H */
diff --git a/libide/ide-workbench-header-bar.c b/libide/ide-workbench-header-bar.c
new file mode 100644
index 0000000..75a159e
--- /dev/null
+++ b/libide/ide-workbench-header-bar.c
@@ -0,0 +1,58 @@
+/* ide-workbench-header-bar.c
+ *
+ * Copyright (C) 2015 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-workbench-header-bar"
+
+#include "ide-application.h"
+#include "ide-workbench-header-bar.h"
+
+typedef struct
+{
+ GtkMenuButton *menu_button;
+} IdeWorkbenchHeaderBarPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (IdeWorkbenchHeaderBar, ide_workbench_header_bar, GTK_TYPE_HEADER_BAR)
+
+GtkWidget *
+ide_workbench_header_bar_new (void)
+{
+ return g_object_new (IDE_TYPE_WORKBENCH_HEADER_BAR, NULL);
+}
+
+static void
+ide_workbench_header_bar_class_init (IdeWorkbenchHeaderBarClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/builder/ui/ide-workbench-header-bar.ui");
+ gtk_widget_class_bind_template_child_private (widget_class, IdeWorkbenchHeaderBar, menu_button);
+}
+
+static void
+ide_workbench_header_bar_init (IdeWorkbenchHeaderBar *self)
+{
+ IdeWorkbenchHeaderBarPrivate *priv = ide_workbench_header_bar_get_instance_private (self);
+ GtkWidget *popover;
+ GMenu *model;
+
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ model = gtk_application_get_menu_by_id (GTK_APPLICATION (IDE_APPLICATION_DEFAULT), "gear-menu");
+ popover = gtk_popover_new_from_model (NULL, G_MENU_MODEL (model));
+ gtk_menu_button_set_popover (priv->menu_button, popover);
+}
diff --git a/src/greeter/gb-greeter-pill-box.h b/libide/ide-workbench-header-bar.h
similarity index 53%
copy from src/greeter/gb-greeter-pill-box.h
copy to libide/ide-workbench-header-bar.h
index 394a333..cb91fa9 100644
--- a/src/greeter/gb-greeter-pill-box.h
+++ b/libide/ide-workbench-header-bar.h
@@ -1,6 +1,6 @@
-/* gb-greeter-pill-box.h
+/* ide-workbench-header-bar.h
*
- * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ * Copyright (C) 2015 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
@@ -16,22 +16,24 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef GB_GREETER_PILL_BOX_H
-#define GB_GREETER_PILL_BOX_H
+#ifndef IDE_WORKBENCH_HEADER_BAR_H
+#define IDE_WORKBENCH_HEADER_BAR_H
#include <gtk/gtk.h>
G_BEGIN_DECLS
-#define GB_TYPE_GREETER_PILL_BOX (gb_greeter_pill_box_get_type())
+#define IDE_TYPE_WORKBENCH_HEADER_BAR (ide_workbench_header_bar_get_type())
-G_DECLARE_FINAL_TYPE (GbGreeterPillBox, gb_greeter_pill_box, GB, GREETER_PILL_BOX, GtkEventBox)
+G_DECLARE_DERIVABLE_TYPE (IdeWorkbenchHeaderBar, ide_workbench_header_bar, IDE, WORKBENCH_HEADER_BAR,
GtkHeaderBar)
-GtkWidget *gb_greeter_pill_box_new (const gchar *label);
-const gchar *gb_greeter_pill_box_get_label (GbGreeterPillBox *self);
-void gb_greeter_pill_box_set_label (GbGreeterPillBox *self,
- const gchar *label);
+struct _IdeWorkbenchHeaderBarClass
+{
+ GtkHeaderBarClass parent;
+};
+
+GtkWidget *ide_workbench_header_bar_new (void);
G_END_DECLS
-#endif /* GB_GREETER_PILL_BOX_H */
+#endif /* IDE_WORKBENCH_HEADER_BAR_H */
diff --git a/libide/ide-workbench-private.h b/libide/ide-workbench-private.h
new file mode 100644
index 0000000..f917bdc
--- /dev/null
+++ b/libide/ide-workbench-private.h
@@ -0,0 +1,59 @@
+/* ide-workbench-private.h
+ *
+ * Copyright (C) 2015 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/>.
+ */
+
+#ifndef IDE_WORKBENCH_PRIVATE_H
+#define IDE_WORKBENCH_PRIVATE_H
+
+#include <libpeas/peas.h>
+
+#include "ide-editor-perspective.h"
+#include "ide-greeter-perspective.h"
+#include "ide-workbench.h"
+
+G_BEGIN_DECLS
+
+struct _IdeWorkbench
+{
+ GtkApplicationWindow parent;
+
+ IdeContext *context;
+ PeasExtensionSet *addins;
+
+ IdePerspective *perspective;
+
+ GtkStack *top_stack;
+ GtkStack *titlebar_stack;
+ IdeEditorPerspective *editor_perspective;
+ IdeGreeterPerspective *greeter_perspective;
+ GtkStack *perspectives_stack;
+ GtkStackSwitcher *perspectives_stack_switcher;
+ GtkPopover *perspectives_popover;
+};
+
+typedef struct
+{
+ GtkCallback callback;
+ gpointer user_data;
+} IdeWorkbenchForeach;
+
+void _ide_workbench_set_context (IdeWorkbench *workbench,
+ IdeContext *context);
+
+G_END_DECLS
+
+#endif /* IDE_WORKBENCH_PRIVATE_H */
diff --git a/libide/ide-workbench.c b/libide/ide-workbench.c
new file mode 100644
index 0000000..231c711
--- /dev/null
+++ b/libide/ide-workbench.c
@@ -0,0 +1,396 @@
+/* ide-workbench.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-workbench"
+
+#include "ide-macros.h"
+#include "ide-window-settings.h"
+#include "ide-workbench.h"
+#include "ide-workbench-addin.h"
+#include "ide-workbench-header-bar.h"
+#include "ide-workbench-private.h"
+
+G_DEFINE_TYPE (IdeWorkbench, ide_workbench, GTK_TYPE_APPLICATION_WINDOW)
+
+enum {
+ PROP_0,
+ PROP_PERSPECTIVE,
+ LAST_PROP
+};
+
+static GParamSpec *properties [LAST_PROP];
+
+static void
+ide_workbench_finalize (GObject *object)
+{
+ IdeWorkbench *self = (IdeWorkbench *)object;
+
+ ide_clear_weak_pointer (&self->perspective);
+ g_clear_object (&self->context);
+
+ G_OBJECT_CLASS (ide_workbench_parent_class)->finalize (object);
+}
+
+static void
+ide_workbench_constructed (GObject *object)
+{
+ G_OBJECT_CLASS (ide_workbench_parent_class)->constructed (object);
+}
+
+static void
+ide_workbench_destroy (GtkWidget *widget)
+{
+ IdeWorkbench *self = (IdeWorkbench *)widget;
+
+ g_assert (IDE_IS_WORKBENCH (self));
+
+ GTK_WIDGET_CLASS (ide_workbench_parent_class)->destroy (widget);
+}
+
+static void
+ide_workbench_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdeWorkbench *self = IDE_WORKBENCH (object);
+
+ switch (prop_id)
+ {
+ case PROP_PERSPECTIVE:
+ g_value_set_object (value, ide_workbench_get_perspective (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_workbench_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdeWorkbench *self = IDE_WORKBENCH (object);
+
+ switch (prop_id)
+ {
+ case PROP_PERSPECTIVE:
+ ide_workbench_set_perspective (self, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_workbench_class_init (IdeWorkbenchClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->constructed = ide_workbench_constructed;
+ object_class->finalize = ide_workbench_finalize;
+ object_class->get_property = ide_workbench_get_property;
+ object_class->set_property = ide_workbench_set_property;
+
+ widget_class->destroy = ide_workbench_destroy;
+
+ properties [PROP_PERSPECTIVE] =
+ g_param_spec_object ("perspective",
+ "Perspective",
+ "Perspective",
+ IDE_TYPE_PERSPECTIVE,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, LAST_PROP, properties);
+
+ gtk_widget_class_set_template_from_resource (widget_class,
+ "/org/gnome/builder/ui/ide-workbench.ui");
+
+ gtk_widget_class_bind_template_child (widget_class, IdeWorkbench, perspectives_stack);
+ gtk_widget_class_bind_template_child (widget_class, IdeWorkbench, perspectives_stack_switcher);
+ gtk_widget_class_bind_template_child (widget_class, IdeWorkbench, titlebar_stack);
+ gtk_widget_class_bind_template_child (widget_class, IdeWorkbench, top_stack);
+}
+
+static void
+ide_workbench_init_greeter (IdeWorkbench *self)
+{
+ g_assert (IDE_IS_WORKBENCH (self));
+
+ self->greeter_perspective = g_object_new (IDE_TYPE_GREETER_PERSPECTIVE,
+ "visible", TRUE,
+ NULL);
+ ide_workbench_add_perspective (self, IDE_PERSPECTIVE (self->greeter_perspective));
+ gtk_container_child_set (GTK_CONTAINER (self->titlebar_stack),
+ ide_perspective_get_titlebar (IDE_PERSPECTIVE (self->greeter_perspective)),
+ "position", 0,
+ NULL);
+ gtk_container_child_set (GTK_CONTAINER (self->top_stack),
+ GTK_WIDGET (self->greeter_perspective),
+ "position", 0,
+ NULL);
+ ide_workbench_set_perspective (self, IDE_PERSPECTIVE (self->greeter_perspective));
+}
+
+static void
+ide_workbench_init_editor (IdeWorkbench *self)
+{
+ g_assert (IDE_IS_WORKBENCH (self));
+
+ self->editor_perspective = g_object_new (IDE_TYPE_EDITOR_PERSPECTIVE,
+ "visible", TRUE,
+ NULL);
+ ide_workbench_add_perspective (self, IDE_PERSPECTIVE (self->editor_perspective));
+}
+
+static void
+ide_workbench_init (IdeWorkbench *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ ide_workbench_init_greeter (self);
+ ide_workbench_init_editor (self);
+ ide_workbench_init_editor (self);
+ ide_window_settings_register (GTK_WINDOW (self));
+
+ gtk_stack_set_visible_child (self->top_stack, GTK_WIDGET (self->greeter_perspective));
+}
+
+static void
+ide_workbench_views_foreach_cb (GtkWidget *widget,
+ gpointer user_data)
+{
+ IdeWorkbenchForeach *foreach_data = user_data;
+
+ g_assert (foreach_data);
+ g_assert (foreach_data->callback);
+
+ foreach_data->callback (widget, foreach_data->user_data);
+}
+
+/**
+ * ide_workbench_views_foreach:
+ * @self: An #IdeWorkbench.
+ * @callback: (scope call): The callback to execute
+ * @user_data: user data for @callback.
+ *
+ * Executes @callback for every #IdeView across all perspectives.
+ */
+void
+ide_workbench_views_foreach (IdeWorkbench *self,
+ GtkCallback callback,
+ gpointer user_data)
+{
+ IdeWorkbenchForeach foreach = { callback, user_data };
+
+ g_return_if_fail (IDE_IS_WORKBENCH (self));
+ g_return_if_fail (callback != NULL);
+
+ gtk_container_foreach (GTK_CONTAINER (self->perspectives_stack),
+ ide_workbench_views_foreach_cb,
+ &foreach);
+}
+
+static void
+ide_workbench_addin_added (PeasExtensionSet *set,
+ PeasPluginInfo *plugin_info,
+ PeasExtension *extension,
+ gpointer user_data)
+{
+ IdeWorkbench *self = user_data;
+
+ g_assert (PEAS_IS_EXTENSION_SET (set));
+ g_assert (plugin_info != NULL);
+ g_assert (IDE_IS_WORKBENCH_ADDIN (extension));
+ g_assert (IDE_IS_WORKBENCH (self));
+
+ ide_workbench_addin_load (IDE_WORKBENCH_ADDIN (extension), self);
+}
+
+static void
+ide_workbench_addin_removed (PeasExtensionSet *set,
+ PeasPluginInfo *plugin_info,
+ PeasExtension *extension,
+ gpointer user_data)
+{
+ IdeWorkbench *self = user_data;
+
+ g_assert (PEAS_IS_EXTENSION_SET (set));
+ g_assert (plugin_info != NULL);
+ g_assert (IDE_IS_WORKBENCH_ADDIN (extension));
+ g_assert (IDE_IS_WORKBENCH (self));
+
+ ide_workbench_addin_unload (IDE_WORKBENCH_ADDIN (extension), self);
+}
+
+/**
+ * ide_workbench_get_context:
+ * @self: An #IdeWorkbench.
+ *
+ * Gets the context associated with the workbench, or %NULL.
+ *
+ * Returns: (transfer none) (nullable): An #IdeContext or %NULL.
+ */
+IdeContext *
+ide_workbench_get_context (IdeWorkbench *self)
+{
+ g_return_val_if_fail (IDE_IS_WORKBENCH (self), NULL);
+
+ return self->context;
+}
+
+void
+_ide_workbench_set_context (IdeWorkbench *self,
+ IdeContext *context)
+{
+ g_return_if_fail (IDE_IS_WORKBENCH (self));
+ g_return_if_fail (IDE_IS_CONTEXT (context));
+ g_return_if_fail (self->context == NULL);
+
+ g_set_object (&self->context, context);
+
+ self->addins = peas_extension_set_new (peas_engine_get_default (),
+ IDE_TYPE_WORKBENCH_ADDIN,
+ NULL);
+
+ g_signal_connect (self->addins,
+ "extension-added",
+ G_CALLBACK (ide_workbench_addin_added),
+ self);
+
+ g_signal_connect (self->addins,
+ "extension-removed",
+ G_CALLBACK (ide_workbench_addin_removed),
+ self);
+
+ peas_extension_set_foreach (self->addins, ide_workbench_addin_added, self);
+
+ ide_workbench_set_perspective (self, IDE_PERSPECTIVE (self->editor_perspective));
+
+ gtk_stack_set_visible_child_name (self->top_stack, "perspectives");
+}
+
+void
+ide_workbench_add_perspective (IdeWorkbench *self,
+ IdePerspective *perspective)
+{
+ const gchar *id;
+ const gchar *title;
+ const gchar *icon_name;
+ GtkStack *stack;
+ GtkWidget *titlebar;
+
+ g_assert (IDE_IS_WORKBENCH (self));
+ g_assert (IDE_IS_PERSPECTIVE (perspective));
+
+ id = ide_perspective_get_id (perspective);
+ title = ide_perspective_get_title (perspective);
+ icon_name = ide_perspective_get_icon_name (perspective);
+
+ if (IDE_IS_GREETER_PERSPECTIVE (perspective))
+ stack = self->top_stack;
+ else
+ stack = self->perspectives_stack;
+
+ gtk_widget_set_hexpand (GTK_WIDGET (perspective), TRUE);
+
+ gtk_container_add_with_properties (GTK_CONTAINER (stack),
+ GTK_WIDGET (perspective),
+ "icon-name", icon_name,
+ "name", id,
+ "needs-attention", FALSE,
+ "title", title,
+ NULL);
+
+ titlebar = ide_perspective_get_titlebar (perspective);
+ if (titlebar == NULL)
+ titlebar = g_object_new (IDE_TYPE_WORKBENCH_HEADER_BAR,
+ "visible", TRUE,
+ NULL);
+
+ gtk_container_add_with_properties (GTK_CONTAINER (self->titlebar_stack), titlebar,
+ "name", id,
+ NULL);
+}
+
+void
+ide_workbench_remove_perspective (IdeWorkbench *self,
+ IdePerspective *perspective)
+{
+ const gchar *id;
+ GtkWidget *titlebar;
+
+ g_assert (IDE_IS_WORKBENCH (self));
+ g_assert (IDE_IS_PERSPECTIVE (perspective));
+ g_assert (gtk_widget_get_parent (GTK_WIDGET (perspective)) ==
+ GTK_WIDGET (self->perspectives_stack));
+
+ id = ide_perspective_get_id (perspective);
+ titlebar = gtk_stack_get_child_by_name (self->titlebar_stack, id);
+
+ gtk_container_remove (GTK_CONTAINER (self->titlebar_stack), titlebar);
+ gtk_container_remove (GTK_CONTAINER (self->perspectives_stack), GTK_WIDGET (perspective));
+}
+
+/**
+ * ide_workbench_get_perspective:
+ * @self: An #IdeWorkbench.
+ *
+ * Gets the current perspective.
+ *
+ * Returns: (transfer none): An #IdePerspective.
+ */
+IdePerspective *
+ide_workbench_get_perspective (IdeWorkbench *self)
+{
+ GtkWidget *visible_child;
+
+ g_return_val_if_fail (IDE_IS_WORKBENCH (self), NULL);
+
+ visible_child = gtk_stack_get_visible_child (self->perspectives_stack);
+
+ return IDE_PERSPECTIVE (visible_child);
+}
+
+void
+ide_workbench_set_perspective (IdeWorkbench *self,
+ IdePerspective *perspective)
+{
+ GActionGroup *actions;
+ const gchar *id;
+ GtkStack *stack;
+
+ g_return_if_fail (IDE_IS_WORKBENCH (self));
+ g_return_if_fail (IDE_IS_PERSPECTIVE (perspective));
+
+ if (IDE_IS_GREETER_PERSPECTIVE (perspective))
+ stack = self->top_stack;
+ else
+ stack = self->perspectives_stack;
+
+ id = ide_perspective_get_id (perspective);
+ gtk_stack_set_visible_child_name (self->titlebar_stack, id);
+ gtk_stack_set_visible_child_name (stack, id);
+
+ actions = ide_perspective_get_actions (perspective);
+ gtk_widget_insert_action_group (GTK_WIDGET (self), "perspective", actions);
+}
diff --git a/libide/ide-workbench.h b/libide/ide-workbench.h
new file mode 100644
index 0000000..af22553
--- /dev/null
+++ b/libide/ide-workbench.h
@@ -0,0 +1,69 @@
+/* ide-workbench.h
+ *
+ * Copyright (C) 2015 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/>.
+ */
+
+#ifndef IDE_WORKBENCH_H
+#define IDE_WORKBENCH_H
+
+#include <gtk/gtk.h>
+
+#include "ide-context.h"
+#include "ide-perspective.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_WORKBENCH (ide_workbench_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeWorkbench, ide_workbench, IDE, WORKBENCH, GtkApplicationWindow)
+
+void ide_workbench_open_async (IdeWorkbench *self,
+ GFile **files,
+ gint n_files,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean ide_workbench_open_finish (IdeWorkbench *self,
+ GAsyncResult *result,
+ GError **error);
+void ide_workbench_save_all_async (IdeWorkbench *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean ide_workbench_save_all_finish (IdeWorkbench *self,
+ GAsyncResult *result,
+ GError **error);
+void ide_workbench_focus (IdeWorkbench *self,
+ GtkWidget *widget);
+void ide_workbench_close (IdeWorkbench *self);
+IdeContext *ide_workbench_get_context (IdeWorkbench *self);
+void ide_workbench_add_perspective (IdeWorkbench *self,
+ IdePerspective *perspective);
+void ide_workbench_remove_perspective (IdeWorkbench *self,
+ IdePerspective *perspective);
+IdePerspective *ide_workbench_get_perspective (IdeWorkbench *self);
+void ide_workbench_set_perspective (IdeWorkbench *self,
+ IdePerspective *perspective);
+gboolean ide_workbench_get_fullscreen (IdeWorkbench *self);
+void ide_workbench_set_fullscreen (IdeWorkbench *self,
+ gboolean fullscreen);
+void ide_workbench_views_foreach (IdeWorkbench *self,
+ GtkCallback callback,
+ gpointer user_data);
+
+G_END_DECLS
+
+#endif /* IDE_WORKBENCH_H */
diff --git a/libide/ide.c b/libide/ide.c
index d1ff190..eb84919 100644
--- a/libide/ide.c
+++ b/libide/ide.c
@@ -18,7 +18,6 @@
#include <girepository.h>
#include <glib/gi18n.h>
-#include <libgit2-glib/ggit.h>
#include <stdlib.h>
#include "gconstructor.h"
@@ -30,9 +29,6 @@
#include "ide-git-vcs.h"
#include "ide-gsettings-file-settings.h"
#include "ide-modelines-file-settings.h"
-#include "ide-internal.h"
-#include "ide-project-miner.h"
-#include "ide-search-provider.h"
#ifdef ENABLE_GJS_SCRIPTING
# include "ide-gjs-script.h"
@@ -42,8 +38,6 @@
# include "ide-pygobject-script.h"
#endif
-#include "modeline-parser.h"
-
static gboolean programNameRead;
static const gchar *programName = "libide";
@@ -78,13 +72,6 @@ ide_set_program_name (const gchar *program_name)
static void
ide_init_ctor (void)
{
- GgitFeatureFlags ggit_flags;
-
- g_irepository_prepend_search_path (LIBDIR"/gnome-builder/girepository-1.0");
-
- g_type_ensure (IDE_TYPE_CONTEXT);
- g_type_ensure (IDE_TYPE_VCS);
-
g_io_extension_point_register (IDE_FILE_SETTINGS_EXTENSION_POINT);
g_io_extension_point_register (IDE_SCRIPT_EXTENSION_POINT);
g_io_extension_point_register (IDE_VCS_EXTENSION_POINT);
@@ -124,27 +111,4 @@ ide_init_ctor (void)
IDE_TYPE_DIRECTORY_VCS,
IDE_VCS_EXTENSION_POINT".directory",
-200);
-
- modeline_parser_init ();
-
- ggit_init ();
-
- ggit_flags = ggit_get_features ();
-
- if ((ggit_flags & GGIT_FEATURE_THREADS) == 0)
- {
- g_error (_("Builder requires libgit2-glib with threading support."));
- exit (EXIT_FAILURE);
- }
-
- if ((ggit_flags & GGIT_FEATURE_SSH) == 0)
- {
- g_error (_("Builder requires libgit2-glib with SSH support."));
- exit (EXIT_FAILURE);
- }
-
- /* TODO: tune what we startup here once we have IdeApplication/IdeWorkbench/etc. */
- _ide_thread_pool_init ();
-
- _ide_battery_monitor_init ();
}
diff --git a/libide/ide.h b/libide/ide.h
index b6bd696..f50106c 100644
--- a/libide/ide.h
+++ b/libide/ide.h
@@ -25,6 +25,7 @@ G_BEGIN_DECLS
#define IDE_INSIDE
+#include "ide-application.h"
#include "ide-back-forward-item.h"
#include "ide-back-forward-list.h"
#include "ide-build-result.h"
@@ -56,10 +57,14 @@ G_BEGIN_DECLS
#include "ide-highlight-engine.h"
#include "ide-highlighter.h"
#include "ide-indenter.h"
+#include "ide-layout-manager.h"
#include "ide-log.h"
#include "ide-macros.h"
#include "ide-object.h"
#include "ide-pattern-spec.h"
+#include "ide-perspective.h"
+#include "ide-preferences.h"
+#include "ide-preferences-addin.h"
#include "ide-process.h"
#include "ide-progress.h"
#include "ide-project.h"
@@ -96,6 +101,7 @@ G_BEGIN_DECLS
#include "ide-unsaved-files.h"
#include "ide-vcs.h"
#include "ide-vcs-uri.h"
+#include "ide-workbench.h"
#include "directory/ide-directory-vcs.h"
#include "doap/ide-doap-person.h"
diff --git a/libide/resources/libide.gresource.xml b/libide/resources/libide.gresource.xml
index 647b0ae..55ee581 100644
--- a/libide/resources/libide.gresource.xml
+++ b/libide/resources/libide.gresource.xml
@@ -18,5 +18,14 @@
<file alias="keybindings/emacs.css">../../data/keybindings/emacs.css</file>
<file alias="keybindings/vim.css">../../data/keybindings/vim.css</file>
+
+ </gresource>
+
+ <gresource prefix="/org/gnome/builder">
+ <file alias="ui/ide-editor-perspective.ui">../../data/ui/ide-editor-perspective.ui</file>
+ <file alias="ui/ide-greeter-perspective.ui">../../data/ui/ide-greeter-perspective.ui</file>
+ <file alias="ui/ide-greeter-project-row.ui">../../data/ui/ide-greeter-project-row.ui</file>
+ <file alias="ui/ide-workbench.ui">../../data/ui/ide-workbench.ui</file>
+ <file alias="ui/ide-workbench-header-bar.ui">../../data/ui/ide-workbench-header-bar.ui</file>
</gresource>
</gresources>
diff --git a/libide/util/ide-window-settings.c b/libide/util/ide-window-settings.c
new file mode 100644
index 0000000..41b8334
--- /dev/null
+++ b/libide/util/ide-window-settings.c
@@ -0,0 +1,154 @@
+/* ide-window-settings.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ide-window-settings.h"
+
+#define GB_WINDOW_MIN_WIDTH 1280
+#define GB_WINDOW_MIN_HEIGHT 720
+#define SAVE_TIMEOUT_SECS 1
+
+static GSettings *settings;
+
+static gboolean
+ide_window_settings__window_save_settings_cb (gpointer data)
+{
+ GtkWindow *window = data;
+ GdkRectangle geom;
+ gboolean maximized;
+
+ g_assert (GTK_IS_WINDOW (window));
+ g_assert (G_IS_SETTINGS (settings));
+
+ g_object_set_data (G_OBJECT (window), "SETTINGS_HANDLER_ID", NULL);
+
+ gtk_window_get_size (window, &geom.width, &geom.height);
+ gtk_window_get_position (window, &geom.x, &geom.y);
+ maximized = gtk_window_is_maximized (window);
+
+ g_settings_set (settings, "window-size", "(ii)", geom.width, geom.height);
+ g_settings_set (settings, "window-position", "(ii)", geom.x, geom.y);
+ g_settings_set_boolean (settings, "window-maximized", maximized);
+
+ return G_SOURCE_REMOVE;
+}
+
+static gboolean
+ide_window_settings__window_configure_event (GtkWindow *window,
+ GdkEventConfigure *event)
+{
+ guint handler;
+
+ g_assert (GTK_IS_WINDOW (window));
+ g_assert (event != NULL);
+ g_assert (G_IS_SETTINGS (settings));
+
+ handler = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (window), "SETTINGS_HANDLER_ID"));
+
+ if (handler == 0)
+ {
+ handler = g_timeout_add_seconds (SAVE_TIMEOUT_SECS,
+ ide_window_settings__window_save_settings_cb,
+ window);
+ g_object_set_data (G_OBJECT (window), "SETTINGS_HANDLER_ID", GINT_TO_POINTER (handler));
+ }
+
+ return GDK_EVENT_PROPAGATE;
+}
+
+static void
+ide_window_settings__window_realize (GtkWindow *window)
+{
+ GdkRectangle geom = { 0 };
+ gboolean maximized = FALSE;
+
+ g_assert (GTK_IS_WINDOW (window));
+ g_assert (G_IS_SETTINGS (settings));
+
+ g_settings_get (settings, "window-position", "(ii)", &geom.x, &geom.y);
+ g_settings_get (settings, "window-size", "(ii)", &geom.width, &geom.height);
+ g_settings_get (settings, "window-maximized", "b", &maximized);
+
+ geom.width = MAX (geom.width, GB_WINDOW_MIN_WIDTH);
+ geom.height = MAX (geom.height, GB_WINDOW_MIN_HEIGHT);
+ gtk_window_set_default_size (window, geom.width, geom.height);
+
+ gtk_window_move (window, geom.x, geom.y);
+
+ if (maximized)
+ gtk_window_maximize (window);
+}
+
+static void
+ide_window_settings__window_destroy (GtkWindow *window)
+{
+ guint handler;
+
+ g_assert (GTK_IS_WINDOW (window));
+ g_assert (G_IS_SETTINGS (settings));
+
+ handler = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (window), "SETTINGS_HANDLER_ID"));
+
+ if (handler != 0)
+ {
+ g_source_remove (handler);
+ g_object_set_data (G_OBJECT (window), "SETTINGS_HANDLER_ID", NULL);
+ }
+
+ g_signal_handlers_disconnect_by_func (window,
+ G_CALLBACK (ide_window_settings__window_configure_event),
+ NULL);
+
+ g_signal_handlers_disconnect_by_func (window,
+ G_CALLBACK (ide_window_settings__window_destroy),
+ NULL);
+
+ g_signal_handlers_disconnect_by_func (window,
+ G_CALLBACK (ide_window_settings__window_realize),
+ NULL);
+
+ g_object_unref (settings);
+}
+
+void
+ide_window_settings_register (GtkWindow *window)
+{
+ if (settings == NULL)
+ {
+ settings = g_settings_new ("org.gnome.builder");
+ g_object_add_weak_pointer (G_OBJECT (settings), (gpointer *)&settings);
+ }
+ else
+ {
+ g_object_ref (settings);
+ }
+
+ g_signal_connect (window,
+ "configure-event",
+ G_CALLBACK (ide_window_settings__window_configure_event),
+ NULL);
+
+ g_signal_connect (window,
+ "destroy",
+ G_CALLBACK (ide_window_settings__window_destroy),
+ NULL);
+
+ g_signal_connect (window,
+ "realize",
+ G_CALLBACK (ide_window_settings__window_realize),
+ NULL);
+}
diff --git a/src/greeter/gb-greeter-pill-box.h b/libide/util/ide-window-settings.h
similarity index 57%
rename from src/greeter/gb-greeter-pill-box.h
rename to libide/util/ide-window-settings.h
index 394a333..a2b6542 100644
--- a/src/greeter/gb-greeter-pill-box.h
+++ b/libide/util/ide-window-settings.h
@@ -1,4 +1,4 @@
-/* gb-greeter-pill-box.h
+/* ide-window-settings.h
*
* Copyright (C) 2015 Christian Hergert <christian hergert me>
*
@@ -16,22 +16,15 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef GB_GREETER_PILL_BOX_H
-#define GB_GREETER_PILL_BOX_H
+#ifndef IDE_WINDOW_SETTINGS_H
+#define IDE_WINDOW_SETTINGS_H
#include <gtk/gtk.h>
G_BEGIN_DECLS
-#define GB_TYPE_GREETER_PILL_BOX (gb_greeter_pill_box_get_type())
-
-G_DECLARE_FINAL_TYPE (GbGreeterPillBox, gb_greeter_pill_box, GB, GREETER_PILL_BOX, GtkEventBox)
-
-GtkWidget *gb_greeter_pill_box_new (const gchar *label);
-const gchar *gb_greeter_pill_box_get_label (GbGreeterPillBox *self);
-void gb_greeter_pill_box_set_label (GbGreeterPillBox *self,
- const gchar *label);
+void ide_window_settings_register (GtkWindow *window);
G_END_DECLS
-#endif /* GB_GREETER_PILL_BOX_H */
+#endif /* IDE_WINDOW_SETTINGS_H */
diff --git a/src/Makefile.am b/src/Makefile.am
index 4e90e31..e6bb8e1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -80,8 +80,6 @@ libgnome_builder_la_SOURCES = \
editor/gb-editor-view-actions.h \
editor/gb-editor-view-addin-private.h \
editor/gb-editor-view-private.h \
- greeter/gb-greeter-pill-box.c \
- greeter/gb-greeter-pill-box.h \
greeter/gb-greeter-project-row.c \
greeter/gb-greeter-project-row.h \
greeter/gb-greeter-window.c \
diff --git a/src/greeter/gb-greeter-project-row.c b/src/greeter/gb-greeter-project-row.c
index fe353d8..da4459a 100644
--- a/src/greeter/gb-greeter-project-row.c
+++ b/src/greeter/gb-greeter-project-row.c
@@ -22,10 +22,10 @@
#include <ide.h>
#include "egg-binding-group.h"
+#include "egg-pill-box.h"
#include "gb-glib.h"
#include "gb-greeter-project-row.h"
-#include "gb-greeter-pill-box.h"
struct _GbGreeterProjectRow
{
@@ -135,7 +135,7 @@ gb_greeter_project_row_add_languages (GbGreeterProjectRow *self,
const gchar *name = languages [i - 1];
GtkWidget *pill;
- pill = g_object_new (GB_TYPE_GREETER_PILL_BOX,
+ pill = g_object_new (EGG_TYPE_PILL_BOX,
"visible", TRUE,
"label", name,
NULL);
diff --git a/src/main.c b/src/main.c
index fc04523..175851b 100644
--- a/src/main.c
+++ b/src/main.c
@@ -28,7 +28,6 @@
#include <ide.h>
#include <locale.h>
-#include "gb-application.h"
#include "gb-icons-resources.h"
int
@@ -58,7 +57,7 @@ main (int argc,
g_resources_register (gb_icons_get_resource ());
- app = g_object_new (GB_TYPE_APPLICATION,
+ app = g_object_new (IDE_TYPE_APPLICATION,
"application-id", "org.gnome.Builder",
"flags", G_APPLICATION_HANDLES_OPEN,
NULL);
diff --git a/src/resources/gnome-builder.gresource.xml b/src/resources/gnome-builder.gresource.xml
index 18862b5..3cf649e 100644
--- a/src/resources/gnome-builder.gresource.xml
+++ b/src/resources/gnome-builder.gresource.xml
@@ -21,7 +21,6 @@
<file alias="ui/gb-editor-settings-widget.ui">../../data/ui/gb-editor-settings-widget.ui</file>
<file alias="ui/gb-editor-tweak-widget.ui">../../data/ui/gb-editor-tweak-widget.ui</file>
<file alias="ui/gb-editor-view.ui">../../data/ui/gb-editor-view.ui</file>
- <file alias="ui/gb-greeter-pill-box.ui">../../data/ui/gb-greeter-pill-box.ui</file>
<file alias="ui/gb-greeter-project-row.ui">../../data/ui/gb-greeter-project-row.ui</file>
<file alias="ui/gb-greeter-window.ui">../../data/ui/gb-greeter-window.ui</file>
<file alias="ui/gb-html-view.ui">../../data/ui/gb-html-view.ui</file>
diff --git a/src/util/gb-glib.h b/src/util/gb-glib.h
index 1942a68..c7f9f36 100644
--- a/src/util/gb-glib.h
+++ b/src/util/gb-glib.h
@@ -42,8 +42,6 @@ G_BEGIN_DECLS
} \
} G_STMT_END
-gchar *gb_date_time_format_for_display (GDateTime *self);
-
G_END_DECLS
#endif /* GB_GLIB_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]