[gnome-builder: 45/139] libide-gui: add new libide-gui static library
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder: 45/139] libide-gui: add new libide-gui static library
- Date: Thu, 10 Jan 2019 04:21:06 +0000 (UTC)
commit 387cf0dc6a13519822f4d0d2b14d1d0009f7e385
Author: Christian Hergert <chergert redhat com>
Date: Wed Jan 9 16:34:57 2019 -0800
libide-gui: add new libide-gui static library
This adds the new refactored UI code in the form of libide-gui. Some of
the main changes to the design include the abstraction of IdeWorkbench
from a GtkWindow to a GtkWindowGroup. It now contains multiple workbench
IdeWorkspace (GtkWindow). This allows for multi-monitor setups so that
users may have multiple views of code and content.
The IdeContext creation also no longer needs to have a project to load
initially. This allows for a non-project mode while still building on
the abstractions provided by IdeContext/IdeObject.
However, plugins should check to see if they're in a project mode and
avoid accessing foundry components in that case.
src/libide/gtk/menus.ui | 266 ---
src/libide/gui/gs-markdown-private.h | 58 +
src/libide/{util => gui}/gs-markdown.c | 4 +-
src/libide/gui/gtk/menus.ui | 86 +
src/libide/{util => gui}/ide-cell-renderer-fancy.c | 2 +-
src/libide/{util => gui}/ide-cell-renderer-fancy.h | 7 +-
src/libide/gui/ide-command-provider.c | 103 +
src/libide/gui/ide-command-provider.h | 66 +
src/libide/gui/ide-command.c | 153 ++
src/libide/gui/ide-command.h | 66 +
src/libide/gui/ide-config-view-addin.c | 46 +
src/libide/gui/ide-config-view-addin.h | 48 +
src/libide/gui/ide-environment-editor-row.c | 278 +++
src/libide/gui/ide-environment-editor-row.h | 37 +
src/libide/gui/ide-environment-editor-row.ui | 49 +
src/libide/gui/ide-environment-editor.c | 317 +++
src/libide/gui/ide-environment-editor.h | 42 +
src/libide/{util => gui}/ide-fancy-tree-view.c | 4 +-
src/libide/{util => gui}/ide-fancy-tree-view.h | 12 +-
src/libide/gui/ide-frame-actions.c | 429 ++++
src/libide/gui/ide-frame-addin.c | 111 +
src/libide/gui/ide-frame-addin.h | 65 +
.../ide-frame-header.c} | 235 +-
.../ide-frame-header.h} | 21 +-
.../ide-frame-header.ui} | 4 +-
.../ide-frame-shortcuts.c} | 43 +-
.../ide-frame-wrapper.c} | 40 +-
.../ide-frame-wrapper.h} | 6 +-
.../{layout/ide-layout-stack.c => gui/ide-frame.c} | 833 +++----
src/libide/gui/ide-frame.h | 84 +
.../ide-layout-stack.ui => gui/ide-frame.ui} | 6 +-
.../ide-grid-actions.c} | 24 +-
.../ide-grid-column-actions.c} | 34 +-
.../ide-grid-column.c} | 188 +-
.../ide-grid-column.h} | 23 +-
.../{layout/ide-layout-grid.c => gui/ide-grid.c} | 780 +++----
src/libide/gui/ide-grid.h | 77 +
.../{util/ide-gtk.c => gui/ide-gui-global.c} | 218 +-
.../{util/ide-gtk.h => gui/ide-gui-global.h} | 31 +-
src/libide/gui/ide-gui-private.h | 103 +
src/libide/gui/ide-header-bar-shortcuts.c | 68 +
src/libide/gui/ide-header-bar.c | 469 ++++
src/libide/gui/ide-header-bar.h | 67 +
src/libide/gui/ide-header-bar.ui | 76 +
src/libide/gui/ide-marked-view.c | 112 +
.../ide-omni-bar.h => gui/ide-marked-view.h} | 15 +-
.../gui/ide-notification-list-box-row-private.h | 38 +
src/libide/gui/ide-notification-list-box-row.c | 377 ++++
src/libide/gui/ide-notification-list-box-row.ui | 112 +
src/libide/gui/ide-notification-stack-private.h | 44 +
src/libide/gui/ide-notification-stack.c | 405 ++++
src/libide/gui/ide-notification-view-private.h | 37 +
src/libide/gui/ide-notification-view.c | 291 +++
src/libide/gui/ide-notification-view.ui | 63 +
.../gui/ide-notifications-button-popover-private.h | 31 +
src/libide/gui/ide-notifications-button-popover.c | 51 +
src/libide/gui/ide-notifications-button.c | 217 ++
src/libide/gui/ide-notifications-button.h | 40 +
src/libide/gui/ide-notifications-button.ui | 32 +
src/libide/gui/ide-omni-bar-addin.c | 89 +
src/libide/gui/ide-omni-bar-addin.h | 55 +
src/libide/gui/ide-omni-bar.c | 619 ++++++
src/libide/gui/ide-omni-bar.h | 56 +
src/libide/gui/ide-omni-bar.ui | 128 ++
.../{layout/ide-layout-view.c => gui/ide-page.c} | 406 ++--
src/libide/gui/ide-page.h | 119 +
src/libide/gui/ide-pane.c | 54 +
src/libide/gui/ide-pane.h | 48 +
.../{layout/ide-layout-pane.c => gui/ide-panel.c} | 49 +-
.../{layout/ide-layout-pane.h => gui/ide-panel.h} | 22 +-
.../ide-layout-pane.ui => gui/ide-panel.ui} | 5 +-
.../{preferences => gui}/ide-preferences-addin.c | 39 +-
.../{preferences => gui}/ide-preferences-addin.h | 3 +-
.../ide-preferences-builtin-private.h} | 0
.../{preferences => gui}/ide-preferences-builtin.c | 16 +-
.../ide-preferences-language-row-private.h} | 0
.../ide-preferences-language-row.c | 14 +-
.../ide-preferences-language-row.ui | 0
src/libide/gui/ide-preferences-surface.c | 136 ++
.../ide-preferences-surface.h} | 13 +-
.../{preferences => gui}/ide-preferences-window.c | 8 +-
.../{preferences => gui}/ide-preferences-window.h | 7 +-
src/libide/gui/ide-preferences-window.ui | 17 +
src/libide/gui/ide-primary-workspace-actions.c | 109 +
src/libide/gui/ide-primary-workspace.c | 141 ++
src/libide/gui/ide-primary-workspace.h | 38 +
src/libide/gui/ide-primary-workspace.ui | 62 +
src/libide/gui/ide-run-button.c | 200 ++
.../{util/ide-dnd.h => gui/ide-run-button.h} | 10 +-
src/libide/gui/ide-run-button.ui | 67 +
src/libide/{search => gui}/ide-search-entry.c | 175 +-
src/libide/{search => gui}/ide-search-entry.h | 6 +-
src/libide/{search => gui}/ide-search-entry.ui | 1 -
src/libide/{session => gui}/ide-session-addin.c | 18 +-
src/libide/{session => gui}/ide-session-addin.h | 13 +-
src/libide/gui/ide-session-private.h | 51 +
src/libide/{session => gui}/ide-session.c | 91 +-
.../ide-shortcut-label-private.h} | 2 +-
src/libide/{layout => gui}/ide-shortcut-label.c | 2 +-
src/libide/gui/ide-shortcuts-window-private.h | 31 +
src/libide/gui/ide-shortcuts-window.c | 48 +
src/libide/gui/ide-shortcuts-window.ui | 547 +++++
src/libide/gui/ide-surface.c | 259 +++
src/libide/gui/ide-surface.h | 67 +
src/libide/gui/ide-surfaces-button.c | 107 +
.../ide-layout.h => gui/ide-surfaces-button.h} | 23 +-
src/libide/{search => gui}/ide-tagged-entry.c | 2 +
src/libide/{search => gui}/ide-tagged-entry.h | 11 +-
src/libide/gui/ide-transient-sidebar.c | 355 +++
src/libide/gui/ide-transient-sidebar.h | 58 +
.../ide-window-settings-private.h} | 2 +-
src/libide/{util => gui}/ide-window-settings.c | 8 +-
src/libide/gui/ide-workbench-addin.c | 402 ++++
src/libide/gui/ide-workbench-addin.h | 159 ++
src/libide/gui/ide-workbench.c | 2299 ++++++++++++++++++++
src/libide/gui/ide-workbench.h | 144 ++
src/libide/{workers => gui}/ide-worker-manager.c | 8 +-
src/libide/{workers => gui}/ide-worker-manager.h | 0
src/libide/{workers => gui}/ide-worker-process.c | 9 +-
src/libide/{workers => gui}/ide-worker-process.h | 0
src/libide/{workers => gui}/ide-worker.c | 4 +-
src/libide/{workers => gui}/ide-worker.h | 4 +-
src/libide/gui/ide-workspace-actions.c | 92 +
src/libide/gui/ide-workspace-addin.c | 118 +
src/libide/gui/ide-workspace-addin.h | 54 +
src/libide/gui/ide-workspace.c | 971 +++++++++
src/libide/gui/ide-workspace.h | 96 +
src/libide/gui/ide-workspace.ui | 23 +
src/libide/gui/libide-gui.gresource.xml | 24 +
src/libide/gui/libide-gui.h | 70 +
src/libide/gui/meson.build | 212 ++
src/libide/layout/ide-layout-grid.h | 80 -
src/libide/layout/ide-layout-private.h | 71 -
src/libide/layout/ide-layout-stack-actions.c | 418 ----
src/libide/layout/ide-layout-stack-addin.c | 130 --
src/libide/layout/ide-layout-stack-addin.h | 62 -
src/libide/layout/ide-layout-stack.h | 88 -
src/libide/layout/ide-layout-transient-sidebar.c | 357 ---
src/libide/layout/ide-layout-transient-sidebar.h | 61 -
src/libide/layout/ide-layout-view.h | 125 --
src/libide/layout/ide-layout.c | 55 -
src/libide/layout/meson.build | 41 -
.../preferences/ide-preferences-perspective.c | 163 --
src/libide/preferences/ide-preferences-window.ui | 26 -
src/libide/preferences/meson.build | 24 -
src/libide/search/ide-search-engine.c | 72 +-
src/libide/session/ide-session.h | 53 -
src/libide/session/meson.build | 14 -
src/libide/util/gs-markdown.h | 58 -
src/libide/util/ide-dnd.c | 46 -
src/libide/util/ide-progress.c | 293 ---
src/libide/util/ide-progress.h | 58 -
src/libide/workbench/ide-omni-bar.c | 881 --------
src/libide/workbench/ide-omni-bar.ui | 613 ------
src/libide/workbench/ide-omni-pausable-row.c | 187 --
src/libide/workbench/ide-omni-pausable-row.h | 36 -
src/libide/workbench/ide-omni-pausable-row.ui | 57 -
src/libide/workbench/ide-perspective.c | 314 ---
src/libide/workbench/ide-perspective.h | 80 -
src/libide/workbench/ide-workbench-actions.c | 362 ---
src/libide/workbench/ide-workbench-addin.c | 262 ---
src/libide/workbench/ide-workbench-addin.h | 96 -
src/libide/workbench/ide-workbench-header-bar.c | 337 ---
src/libide/workbench/ide-workbench-header-bar.h | 73 -
src/libide/workbench/ide-workbench-header-bar.ui | 119 -
src/libide/workbench/ide-workbench-message.c | 226 --
src/libide/workbench/ide-workbench-message.h | 56 -
src/libide/workbench/ide-workbench-message.ui | 43 -
src/libide/workbench/ide-workbench-open.c | 539 -----
src/libide/workbench/ide-workbench-private.h | 68 -
src/libide/workbench/ide-workbench-shortcuts.c | 147 --
src/libide/workbench/ide-workbench.c | 1143 ----------
src/libide/workbench/ide-workbench.h | 151 --
src/libide/workbench/ide-workbench.ui | 64 -
src/libide/workbench/meson.build | 32 -
src/libide/workers/meson.build | 20 -
176 files changed, 14777 insertions(+), 10064 deletions(-)
---
diff --git a/src/libide/gui/gs-markdown-private.h b/src/libide/gui/gs-markdown-private.h
new file mode 100644
index 000000000..aae18a6aa
--- /dev/null
+++ b/src/libide/gui/gs-markdown-private.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright 2008-2013 Richard Hughes <richard hughsie com>
+ * Copyright 2015 Kalev Lember <klember redhat com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __GS_MARKDOWN_H
+#define __GS_MARKDOWN_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_MARKDOWN (gs_markdown_get_type ())
+
+G_DECLARE_FINAL_TYPE (GsMarkdown, gs_markdown, GS, MARKDOWN, GObject)
+
+typedef enum {
+ GS_MARKDOWN_OUTPUT_TEXT,
+ GS_MARKDOWN_OUTPUT_PANGO,
+ GS_MARKDOWN_OUTPUT_HTML,
+ GS_MARKDOWN_OUTPUT_LAST
+} GsMarkdownOutputKind;
+
+GsMarkdown *gs_markdown_new (GsMarkdownOutputKind output);
+void gs_markdown_set_max_lines (GsMarkdown *self,
+ gint max_lines);
+void gs_markdown_set_smart_quoting (GsMarkdown *self,
+ gboolean smart_quoting);
+void gs_markdown_set_escape (GsMarkdown *self,
+ gboolean escape);
+void gs_markdown_set_autocode (GsMarkdown *self,
+ gboolean autocode);
+void gs_markdown_set_autolinkify (GsMarkdown *self,
+ gboolean autolinkify);
+gchar *gs_markdown_parse (GsMarkdown *self,
+ const gchar *text);
+
+G_END_DECLS
+
+#endif /* __GS_MARKDOWN_H */
+
diff --git a/src/libide/util/gs-markdown.c b/src/libide/gui/gs-markdown.c
similarity index 99%
rename from src/libide/util/gs-markdown.c
rename to src/libide/gui/gs-markdown.c
index 168989099..ed8eb30d7 100644
--- a/src/libide/util/gs-markdown.c
+++ b/src/libide/gui/gs-markdown.c
@@ -25,7 +25,7 @@
#include <string.h>
#include <glib.h>
-#include "gs-markdown.h"
+#include "gs-markdown-private.h"
/*******************************************************************************
*
@@ -46,6 +46,8 @@
* been run against any conformance tests. The parsing is single pass, with
* a simple enumerated interpretor mode and a single line back-memory.
*
+ *
+ * Since: 3.32
******************************************************************************/
typedef enum {
diff --git a/src/libide/gui/gtk/menus.ui b/src/libide/gui/gtk/menus.ui
new file mode 100644
index 000000000..99e8390ef
--- /dev/null
+++ b/src/libide/gui/gtk/menus.ui
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <menu id="ide-primary-workspace-surfaces-menu">
+ <section id="ide-primary-workspace-surfaces-menu-section">
+ <attribute name="label" translatable="yes">Switch Surface</attribute>
+ </section>
+ </menu>
+ <menu id="ide-primary-workspace-menu">
+ <section id="ide-primary-workspace-menu-projects-section"/>
+ <section id="ide-primary-workspace-menu-placeholder1"/>
+ <section id="ide-primary-workspace-menu-open-section">
+ <item>
+ <attribute name="id">ide-primary-workspace-menu-open</attribute>
+ <attribute name="label" translatable="yes">Open File…</attribute>
+ <attribute name="action">workbench.open</attribute>
+ <attribute name="accel"><primary>o</attribute>
+ </item>
+ </section>
+ <section id="ide-primary-workspace-menu-placeholder2"/>
+ <section id="ide-primary-workspace-menu-close-section">
+ <item>
+ <attribute name="id">ide-primary-workspace-menu-close-project</attribute>
+ <attribute name="label" translatable="yes">Close Project</attribute>
+ <attribute name="action">workbench.close</attribute>
+ </item>
+ </section>
+ <section id="ide-primary-workspace-menu-placeholder3"/>
+ <section id="ide-primary-workspace-menu-app-section">
+ <item>
+ <attribute name="id">ide-primary-workspace-menu-preferences</attribute>
+ <attribute name="label" translatable="yes">Preferences</attribute>
+ <attribute name="action">app.preferences</attribute>
+ <attribute name="accel"><primary>comma</attribute>
+ </item>
+ <item>
+ <attribute name="id">ide-primary-workspace-menu-shortcuts</attribute>
+ <attribute name="label" translatable="yes">Keyboard Shortcuts</attribute>
+ <attribute name="action">app.shortcuts</attribute>
+ <attribute name="accel"><primary>question</attribute>
+ </item>
+ <item>
+ <attribute name="id">ide-primary-workspace-menu-help</attribute>
+ <attribute name="label" translatable="yes">Help</attribute>
+ <attribute name="action">app.help</attribute>
+ <attribute name="accel">F1</attribute>
+ </item>
+ <item>
+ <attribute name="id">ide-primary-workspace-menu-about</attribute>
+ <attribute name="label" translatable="yes">About Builder</attribute>
+ <attribute name="action">app.about</attribute>
+ </item>
+ </section>
+ <section id="ide-primary-workspace-menu-quit-section">
+ <item>
+ <attribute name="id">ide-primary-workspace-menu-quit</attribute>
+ <attribute name="label" translatable="yes">_Quit</attribute>
+ <attribute name="action">app.quit</attribute>
+ </item>
+ </section>
+ </menu>
+ <menu id="ide-primary-workspace-new-menu">
+ <section id="new-document-section">
+ </section>
+ <section id="open-document-section">
+ <item>
+ <attribute name="id">open-file</attribute>
+ <attribute name="label" translatable="yes">Open File…</attribute>
+ <attribute name="action">workbench.open</attribute>
+ </item>
+ </section>
+ </menu>
+ <menu id="run-menu">
+ <section id="run-menu-section">
+ <attribute name="label" translatable="yes">Run Options</attribute>
+ <item>
+ <attribute name="id">default-run-handler</attribute>
+ <attribute name="action">run-manager.run-with-handler</attribute>
+ <attribute name="target">run</attribute>
+ <attribute name="label" translatable="yes">Run</attribute>
+ <attribute name="verb-icon-name">media-playback-start-symbolic</attribute>
+ <attribute name="accel"><Control>F5</attribute>
+ </item>
+ </section>
+ </menu>
+</interface>
+
diff --git a/src/libide/util/ide-cell-renderer-fancy.c b/src/libide/gui/ide-cell-renderer-fancy.c
similarity index 99%
rename from src/libide/util/ide-cell-renderer-fancy.c
rename to src/libide/gui/ide-cell-renderer-fancy.c
index 5c7dc33a0..212cb58f0 100644
--- a/src/libide/util/ide-cell-renderer-fancy.c
+++ b/src/libide/gui/ide-cell-renderer-fancy.c
@@ -22,7 +22,7 @@
#include "config.h"
-#include "util/ide-cell-renderer-fancy.h"
+#include "ide-cell-renderer-fancy.h"
#define TITLE_SPACING 3
diff --git a/src/libide/util/ide-cell-renderer-fancy.h b/src/libide/gui/ide-cell-renderer-fancy.h
similarity index 92%
rename from src/libide/util/ide-cell-renderer-fancy.h
rename to src/libide/gui/ide-cell-renderer-fancy.h
index 5d6e6bd6b..2d768612f 100644
--- a/src/libide/util/ide-cell-renderer-fancy.h
+++ b/src/libide/gui/ide-cell-renderer-fancy.h
@@ -20,9 +20,12 @@
#pragma once
-#include <gtk/gtk.h>
+#if !defined (IDE_GUI_INSIDE) && !defined (IDE_GUI_COMPILATION)
+# error "Only <libide-gui.h> can be included directly."
+#endif
-#include "ide-version-macros.h"
+#include <gtk/gtk.h>
+#include <libide-core.h>
G_BEGIN_DECLS
diff --git a/src/libide/gui/ide-command-provider.c b/src/libide/gui/ide-command-provider.c
new file mode 100644
index 000000000..8b0177ab9
--- /dev/null
+++ b/src/libide/gui/ide-command-provider.c
@@ -0,0 +1,103 @@
+/* ide-command-provider.c
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-command-provider"
+
+#include "config.h"
+
+#include "ide-command-provider.h"
+
+G_DEFINE_INTERFACE (IdeCommandProvider, ide_command_provider, G_TYPE_OBJECT)
+
+static void
+ide_command_provider_real_query_async (IdeCommandProvider *self,
+ IdeWorkspace *workspace,
+ const gchar *typed_text,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_task_report_new_error (self, callback, user_data,
+ ide_command_provider_real_query_async,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ "Querying is not supported by this provider");
+}
+
+static GPtrArray *
+ide_command_provider_real_query_finish (IdeCommandProvider *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+static void
+ide_command_provider_default_init (IdeCommandProviderInterface *iface)
+{
+ iface->query_async = ide_command_provider_real_query_async;
+ iface->query_finish = ide_command_provider_real_query_finish;
+}
+
+void
+ide_command_provider_query_async (IdeCommandProvider *self,
+ IdeWorkspace *workspace,
+ const gchar *typed_text,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (IDE_IS_COMMAND_PROVIDER (self));
+ g_return_if_fail (IDE_IS_WORKSPACE (workspace));
+ g_return_if_fail (typed_text != NULL);
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ IDE_COMMAND_PROVIDER_GET_IFACE (self)->query_async (self,
+ workspace,
+ typed_text,
+ cancellable,
+ callback,
+ user_data);
+}
+
+/**
+ * ide_command_provider_query_finish:
+ * @self: a #IdeCommandProvider
+ * @result: a #GAsyncResult
+ * @error: a location for a #GError, or %NULL
+ *
+ * Completes an asynchronous request to locate all the commands matching the
+ * users typed text.
+ *
+ * Returns: (transfer full) (element-type IdeCommand): a #GPtrArray of
+ * #IdeCommand, or %NULL.
+ *
+ * Since: 3.32
+ */
+GPtrArray *
+ide_command_provider_query_finish (IdeCommandProvider *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (IDE_IS_COMMAND_PROVIDER (self), NULL);
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
+
+ return IDE_COMMAND_PROVIDER_GET_IFACE (self)->query_finish (self, result, error);
+}
diff --git a/src/libide/gui/ide-command-provider.h b/src/libide/gui/ide-command-provider.h
new file mode 100644
index 000000000..5fc08f06b
--- /dev/null
+++ b/src/libide/gui/ide-command-provider.h
@@ -0,0 +1,66 @@
+/* ide-command-provider.h
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#if !defined (IDE_GUI_INSIDE) && !defined (IDE_GUI_COMPILATION)
+# error "Only <libide-gui.h> can be included directly."
+#endif
+
+#include <libide-core.h>
+
+#include "ide-command.h"
+#include "ide-workspace.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_COMMAND_PROVIDER (ide_command_provider_get_type())
+
+IDE_AVAILABLE_IN_3_32
+G_DECLARE_INTERFACE (IdeCommandProvider, ide_command_provider, IDE, COMMAND_PROVIDER, GObject)
+
+struct _IdeCommandProviderInterface
+{
+ GTypeInterface parent_iface;
+
+ void (*query_async) (IdeCommandProvider *self,
+ IdeWorkspace *workspace,
+ const gchar *typed_text,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ GPtrArray *(*query_finish) (IdeCommandProvider *self,
+ GAsyncResult *result,
+ GError **error);
+};
+
+IDE_AVAILABLE_IN_3_32
+void ide_command_provider_query_async (IdeCommandProvider *self,
+ IdeWorkspace *workspace,
+ const gchar *typed_text,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+IDE_AVAILABLE_IN_3_32
+GPtrArray *ide_command_provider_query_finish (IdeCommandProvider *self,
+ GAsyncResult *result,
+ GError **error);
+
+G_END_DECLS
diff --git a/src/libide/gui/ide-command.c b/src/libide/gui/ide-command.c
new file mode 100644
index 000000000..8d99d7638
--- /dev/null
+++ b/src/libide/gui/ide-command.c
@@ -0,0 +1,153 @@
+/* ide-command.c
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-command"
+
+#include "config.h"
+
+#include "ide-command.h"
+
+G_DEFINE_INTERFACE (IdeCommand, ide_command, IDE_TYPE_OBJECT)
+
+static void
+ide_command_real_run_async (IdeCommand *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+
+ g_return_if_fail (IDE_IS_COMMAND (self));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_source_tag (task, ide_command_real_run_async);
+ g_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ "The operation is not supported");
+}
+
+static gboolean
+ide_command_real_run_finish (IdeCommand *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (IDE_IS_COMMAND (self), FALSE);
+ g_return_val_if_fail (G_IS_TASK (result), FALSE);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+ide_command_default_init (IdeCommandInterface *iface)
+{
+ iface->run_async = ide_command_real_run_async;
+ iface->run_finish = ide_command_real_run_finish;
+}
+
+/**
+ * ide_command_run_async:
+ * @self: an #IdeCommand
+ * @cancellable: (nullable): a #GCancellable
+ * @callback: a #GAsyncReadyCallback to execute upon completion
+ * @user_data: closure data for @callback
+ *
+ * Runs the command, asynchronously.
+ *
+ * Use ide_command_run_finish() to get the result of the operation.
+ *
+ * Since: 3.32
+ */
+void
+ide_command_run_async (IdeCommand *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (IDE_IS_COMMAND (self));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ IDE_COMMAND_GET_IFACE (self)->run_async (self, cancellable, callback, user_data);
+}
+
+/**
+ * ide_command_run_finish:
+ * @self: an #IdeCommand
+ * @result: a #GAsyncResult provided to callback
+ * @error: a location for a #GError, or %NULL
+ *
+ * Returns: %TRUE if the command was successful; otherwise %FALSE
+ * and @error is set.
+ *
+ * Since: 3.32
+ */
+gboolean
+ide_command_run_finish (IdeCommand *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (IDE_IS_COMMAND (self), FALSE);
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+ return IDE_COMMAND_GET_IFACE (self)->run_finish (self, result, error);
+}
+
+/**
+ * ide_command_get_title:
+ * @self: a #IdeCommand
+ *
+ * Gets the title for the command.
+ *
+ * Returns: a string containing the title
+ *
+ * Since: 3.32
+ */
+gchar *
+ide_command_get_title (IdeCommand *self)
+{
+ g_return_val_if_fail (IDE_IS_COMMAND (self), NULL);
+
+ if (IDE_COMMAND_GET_IFACE (self)->get_title)
+ return IDE_COMMAND_GET_IFACE (self)->get_title (self);
+
+ return NULL;
+}
+
+/**
+ * ide_command_get_subtitle:
+ * @self: a #IdeCommand
+ *
+ * Gets the subtitle for the command.
+ *
+ * Returns: a string containing the subtitle
+ *
+ * Since: 3.32
+ */
+gchar *
+ide_command_get_subtitle (IdeCommand *self)
+{
+ g_return_val_if_fail (IDE_IS_COMMAND (self), NULL);
+
+ if (IDE_COMMAND_GET_IFACE (self)->get_subtitle)
+ return IDE_COMMAND_GET_IFACE (self)->get_subtitle (self);
+
+ return NULL;
+}
diff --git a/src/libide/gui/ide-command.h b/src/libide/gui/ide-command.h
new file mode 100644
index 000000000..0b9f389c7
--- /dev/null
+++ b/src/libide/gui/ide-command.h
@@ -0,0 +1,66 @@
+/* ide-command.h
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#if !defined (IDE_GUI_INSIDE) && !defined (IDE_GUI_COMPILATION)
+# error "Only <libide-gui.h> can be included directly."
+#endif
+
+#include <libide-core.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_COMMAND (ide_command_get_type())
+
+IDE_AVAILABLE_IN_3_32
+G_DECLARE_INTERFACE (IdeCommand, ide_command, IDE, COMMAND, IdeObject)
+
+struct _IdeCommandInterface
+{
+ GTypeInterface parent_iface;
+
+ gchar *(*get_title) (IdeCommand *self);
+ gchar *(*get_subtitle) (IdeCommand *self);
+ void (*run_async) (IdeCommand *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (*run_finish) (IdeCommand *self,
+ GAsyncResult *result,
+ GError **error);
+};
+
+IDE_AVAILABLE_IN_3_32
+gchar *ide_command_get_title (IdeCommand *self);
+IDE_AVAILABLE_IN_3_32
+gchar *ide_command_get_subtitle (IdeCommand *self);
+IDE_AVAILABLE_IN_3_32
+void ide_command_run_async (IdeCommand *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+IDE_AVAILABLE_IN_3_32
+gboolean ide_command_run_finish (IdeCommand *self,
+ GAsyncResult *result,
+ GError **error);
+
+
+G_END_DECLS
diff --git a/src/libide/gui/ide-config-view-addin.c b/src/libide/gui/ide-config-view-addin.c
new file mode 100644
index 000000000..eefc65ec1
--- /dev/null
+++ b/src/libide/gui/ide-config-view-addin.c
@@ -0,0 +1,46 @@
+/* ide-config-view-addin.c
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-config-view-addin"
+
+#include "config.h"
+
+#include "ide-config-view-addin.h"
+
+G_DEFINE_INTERFACE (IdeConfigViewAddin, ide_config_view_addin, G_TYPE_OBJECT)
+
+static void
+ide_config_view_addin_default_init (IdeConfigViewAddinInterface *iface)
+{
+}
+
+void
+ide_config_view_addin_load (IdeConfigViewAddin *self,
+ DzlPreferences *preferences,
+ IdeConfiguration *configuration)
+{
+ g_return_if_fail (IDE_IS_MAIN_THREAD ());
+ g_return_if_fail (IDE_IS_CONFIG_VIEW_ADDIN (self));
+ g_return_if_fail (DZL_IS_PREFERENCES (preferences));
+ g_return_if_fail (IDE_IS_CONFIGURATION (configuration));
+
+ if (IDE_CONFIG_VIEW_ADDIN_GET_IFACE (self)->load)
+ IDE_CONFIG_VIEW_ADDIN_GET_IFACE (self)->load (self, preferences, configuration);
+}
diff --git a/src/libide/gui/ide-config-view-addin.h b/src/libide/gui/ide-config-view-addin.h
new file mode 100644
index 000000000..8786c80e6
--- /dev/null
+++ b/src/libide/gui/ide-config-view-addin.h
@@ -0,0 +1,48 @@
+/* ide-config-view-addin.h
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <dazzle.h>
+#include <libide-core.h>
+#include <libide-foundry.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_CONFIG_VIEW_ADDIN (ide_config_view_addin_get_type())
+
+IDE_AVAILABLE_IN_3_32
+G_DECLARE_INTERFACE (IdeConfigViewAddin, ide_config_view_addin, IDE, CONFIG_VIEW_ADDIN, GObject)
+
+struct _IdeConfigViewAddinInterface
+{
+ GTypeInterface parent_iface;
+
+ void (*load) (IdeConfigViewAddin *self,
+ DzlPreferences *preferences,
+ IdeConfiguration *configuration);
+};
+
+IDE_AVAILABLE_IN_3_32
+void ide_config_view_addin_load (IdeConfigViewAddin *self,
+ DzlPreferences *preferences,
+ IdeConfiguration *configuration);
+
+G_END_DECLS
diff --git a/src/libide/gui/ide-environment-editor-row.c b/src/libide/gui/ide-environment-editor-row.c
new file mode 100644
index 000000000..5e39f9da1
--- /dev/null
+++ b/src/libide/gui/ide-environment-editor-row.c
@@ -0,0 +1,278 @@
+/* ide-environment-editor-row.c
+ *
+ * Copyright 2016-2019 Christian Hergert <christian hergert me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-environment-editor-row"
+
+#include "config.h"
+
+#include "ide-environment-editor-row.h"
+
+struct _IdeEnvironmentEditorRow
+{
+ GtkListBoxRow parent_instance;
+
+ IdeEnvironmentVariable *variable;
+
+ GtkEntry *key_entry;
+ GtkEntry *value_entry;
+ GtkButton *delete_button;
+
+ GBinding *key_binding;
+ GBinding *value_binding;
+};
+
+enum {
+ PROP_0,
+ PROP_VARIABLE,
+ LAST_PROP
+};
+
+enum {
+ DELETE,
+ LAST_SIGNAL
+};
+
+G_DEFINE_TYPE (IdeEnvironmentEditorRow, ide_environment_editor_row, GTK_TYPE_LIST_BOX_ROW)
+
+static GParamSpec *properties [LAST_PROP];
+static guint signals [LAST_SIGNAL];
+
+static gboolean
+null_safe_mapping (GBinding *binding,
+ const GValue *from_value,
+ GValue *to_value,
+ gpointer user_data)
+{
+ const gchar *str = g_value_get_string (from_value);
+ g_value_set_string (to_value, str ?: "");
+ return TRUE;
+}
+
+static void
+ide_environment_editor_row_connect (IdeEnvironmentEditorRow *self)
+{
+ g_assert (IDE_IS_ENVIRONMENT_EDITOR_ROW (self));
+ g_assert (IDE_IS_ENVIRONMENT_VARIABLE (self->variable));
+
+ self->key_binding =
+ g_object_bind_property_full (self->variable, "key", self->key_entry, "text",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
+ null_safe_mapping, NULL, NULL, NULL);
+
+ self->value_binding =
+ g_object_bind_property_full (self->variable, "value", self->value_entry, "text",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
+ null_safe_mapping, NULL, NULL, NULL);
+}
+
+static void
+ide_environment_editor_row_disconnect (IdeEnvironmentEditorRow *self)
+{
+ g_assert (IDE_IS_ENVIRONMENT_EDITOR_ROW (self));
+ g_assert (IDE_IS_ENVIRONMENT_VARIABLE (self->variable));
+
+ g_clear_pointer (&self->key_binding, g_binding_unbind);
+ g_clear_pointer (&self->value_binding, g_binding_unbind);
+}
+
+static void
+delete_button_clicked (GtkButton *button,
+ IdeEnvironmentEditorRow *self)
+{
+ g_assert (GTK_IS_BUTTON (button));
+ g_assert (IDE_IS_ENVIRONMENT_EDITOR_ROW (self));
+
+ g_signal_emit (self, signals [DELETE], 0);
+}
+
+static void
+key_entry_activate (GtkWidget *entry,
+ IdeEnvironmentEditorRow *self)
+{
+ g_assert (GTK_IS_ENTRY (entry));
+ g_assert (IDE_IS_ENVIRONMENT_EDITOR_ROW (self));
+
+ gtk_widget_grab_focus (GTK_WIDGET (self->value_entry));
+}
+
+static void
+value_entry_activate (GtkWidget *entry,
+ IdeEnvironmentEditorRow *self)
+{
+ GtkWidget *parent;
+
+ g_assert (GTK_IS_ENTRY (entry));
+ g_assert (IDE_IS_ENVIRONMENT_EDITOR_ROW (self));
+
+ gtk_widget_grab_focus (GTK_WIDGET (self));
+ parent = gtk_widget_get_ancestor (GTK_WIDGET (self), GTK_TYPE_LIST_BOX);
+ g_signal_emit_by_name (parent, "move-cursor", GTK_MOVEMENT_DISPLAY_LINES, 1);
+}
+
+static void
+ide_environment_editor_row_destroy (GtkWidget *widget)
+{
+ IdeEnvironmentEditorRow *self = (IdeEnvironmentEditorRow *)widget;
+
+ if (self->variable != NULL)
+ {
+ ide_environment_editor_row_disconnect (self);
+ g_clear_object (&self->variable);
+ }
+
+ GTK_WIDGET_CLASS (ide_environment_editor_row_parent_class)->destroy (widget);
+}
+
+static void
+ide_environment_editor_row_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdeEnvironmentEditorRow *self = IDE_ENVIRONMENT_EDITOR_ROW (object);
+
+ switch (prop_id)
+ {
+ case PROP_VARIABLE:
+ g_value_set_object (value, ide_environment_editor_row_get_variable (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_environment_editor_row_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdeEnvironmentEditorRow *self = IDE_ENVIRONMENT_EDITOR_ROW (object);
+
+ switch (prop_id)
+ {
+ case PROP_VARIABLE:
+ ide_environment_editor_row_set_variable (self, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_environment_editor_row_class_init (IdeEnvironmentEditorRowClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->get_property = ide_environment_editor_row_get_property;
+ object_class->set_property = ide_environment_editor_row_set_property;
+
+ widget_class->destroy = ide_environment_editor_row_destroy;
+
+ gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/libide-gui/ui/ide-environment-editor-row.ui");
+ gtk_widget_class_bind_template_child (widget_class, IdeEnvironmentEditorRow, delete_button);
+ gtk_widget_class_bind_template_child (widget_class, IdeEnvironmentEditorRow, key_entry);
+ gtk_widget_class_bind_template_child (widget_class, IdeEnvironmentEditorRow, value_entry);
+
+ properties [PROP_VARIABLE] =
+ g_param_spec_object ("variable",
+ "Variable",
+ "Variable",
+ IDE_TYPE_ENVIRONMENT_VARIABLE,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, LAST_PROP, properties);
+
+ signals [DELETE] =
+ g_signal_new ("delete",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
+}
+
+static void
+ide_environment_editor_row_init (IdeEnvironmentEditorRow *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ g_signal_connect (self->delete_button,
+ "clicked",
+ G_CALLBACK (delete_button_clicked),
+ self);
+
+ g_signal_connect (self->key_entry,
+ "activate",
+ G_CALLBACK (key_entry_activate),
+ self);
+
+ g_signal_connect (self->value_entry,
+ "activate",
+ G_CALLBACK (value_entry_activate),
+ self);
+}
+
+/**
+ * ide_environment_editor_row_get_variable:
+ *
+ * Returns: (transfer none) (nullable): An #IdeEnvironmentVariable.
+ */
+IdeEnvironmentVariable *
+ide_environment_editor_row_get_variable (IdeEnvironmentEditorRow *self)
+{
+ g_return_val_if_fail (IDE_IS_ENVIRONMENT_EDITOR_ROW (self), NULL);
+
+ return self->variable;
+}
+
+void
+ide_environment_editor_row_set_variable (IdeEnvironmentEditorRow *self,
+ IdeEnvironmentVariable *variable)
+{
+ g_return_if_fail (IDE_IS_ENVIRONMENT_EDITOR_ROW (self));
+ g_return_if_fail (!variable || IDE_IS_ENVIRONMENT_VARIABLE (variable));
+
+ if (variable != self->variable)
+ {
+ if (self->variable != NULL)
+ {
+ ide_environment_editor_row_disconnect (self);
+ g_clear_object (&self->variable);
+ }
+
+ if (variable != NULL)
+ {
+ self->variable = g_object_ref (variable);
+ ide_environment_editor_row_connect (self);
+ }
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_VARIABLE]);
+ }
+}
+
+void
+ide_environment_editor_row_start_editing (IdeEnvironmentEditorRow *self)
+{
+ g_return_if_fail (IDE_IS_ENVIRONMENT_EDITOR_ROW (self));
+
+ gtk_widget_grab_focus (GTK_WIDGET (self->key_entry));
+}
diff --git a/src/libide/gui/ide-environment-editor-row.h b/src/libide/gui/ide-environment-editor-row.h
new file mode 100644
index 000000000..01ae2b83e
--- /dev/null
+++ b/src/libide/gui/ide-environment-editor-row.h
@@ -0,0 +1,37 @@
+/* ide-environment-editor-row.h
+ *
+ * Copyright 2016-2019 Christian Hergert <christian hergert me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+#include <libide-threading.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_ENVIRONMENT_EDITOR_ROW (ide_environment_editor_row_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeEnvironmentEditorRow, ide_environment_editor_row, IDE, ENVIRONMENT_EDITOR_ROW,
GtkListBoxRow)
+
+IdeEnvironmentVariable *ide_environment_editor_row_get_variable (IdeEnvironmentEditorRow *self);
+void ide_environment_editor_row_set_variable (IdeEnvironmentEditorRow *self,
+ IdeEnvironmentVariable *variable);
+void ide_environment_editor_row_start_editing (IdeEnvironmentEditorRow *self);
+
+G_END_DECLS
diff --git a/src/libide/gui/ide-environment-editor-row.ui b/src/libide/gui/ide-environment-editor-row.ui
new file mode 100644
index 000000000..56b176a2f
--- /dev/null
+++ b/src/libide/gui/ide-environment-editor-row.ui
@@ -0,0 +1,49 @@
+<interface>
+ <template class="IdeEnvironmentEditorRow" parent="GtkListBoxRow">
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">horizontal</property>
+ <property name="spacing">12</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkEntry" id="key_entry">
+ <property name="has-frame">false</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="eq_label">
+ <property name="label">=</property>
+ <property name="visible">true</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkEntry" id="value_entry">
+ <property name="hexpand">true</property>
+ <property name="has-frame">false</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="delete_button">
+ <property name="visible">true</property>
+ <property name="tooltip-text" translatable="yes">Remove environment variable</property>
+ <style>
+ <class name="image-button"/>
+ <class name="flat"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="icon-name">list-remove-symbolic</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/src/libide/gui/ide-environment-editor.c b/src/libide/gui/ide-environment-editor.c
new file mode 100644
index 000000000..31c4e5b27
--- /dev/null
+++ b/src/libide/gui/ide-environment-editor.c
@@ -0,0 +1,317 @@
+/* ide-environment-editor.c
+ *
+ * Copyright 2016-2019 Christian Hergert <christian hergert me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-environment-editor"
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include "ide-environment-editor.h"
+#include "ide-environment-editor-row.h"
+
+struct _IdeEnvironmentEditor
+{
+ GtkListBox parent_instance;
+ IdeEnvironment *environment;
+ GtkWidget *dummy_row;
+
+ IdeEnvironmentVariable *dummy;
+};
+
+G_DEFINE_TYPE (IdeEnvironmentEditor, ide_environment_editor, GTK_TYPE_LIST_BOX)
+
+enum {
+ PROP_0,
+ PROP_ENVIRONMENT,
+ LAST_PROP
+};
+
+static GParamSpec *properties [LAST_PROP];
+
+static void
+ide_environment_editor_delete_row (IdeEnvironmentEditor *self,
+ IdeEnvironmentEditorRow *row)
+{
+ IdeEnvironmentVariable *variable;
+
+ g_assert (IDE_IS_ENVIRONMENT_EDITOR (self));
+ g_assert (IDE_IS_ENVIRONMENT_EDITOR_ROW (row));
+
+ variable = ide_environment_editor_row_get_variable (row);
+ ide_environment_remove (self->environment, variable);
+}
+
+static GtkWidget *
+ide_environment_editor_create_dummy_row (IdeEnvironmentEditor *self)
+{
+ GtkWidget *row;
+ GtkWidget *label;
+
+ g_assert (IDE_IS_ENVIRONMENT_EDITOR (self));
+
+ label = g_object_new (GTK_TYPE_LABEL,
+ "label", _("New variable…"),
+ "visible", TRUE,
+ "xalign", 0.0f,
+ NULL);
+ gtk_style_context_add_class (gtk_widget_get_style_context (label), "dim-label");
+
+ row = g_object_new (GTK_TYPE_LIST_BOX_ROW,
+ "child", label,
+ "visible", TRUE,
+ NULL);
+
+ return row;
+}
+
+static GtkWidget *
+ide_environment_editor_create_row (gpointer item,
+ gpointer user_data)
+{
+ IdeEnvironmentVariable *variable = item;
+ IdeEnvironmentEditor *self = user_data;
+ IdeEnvironmentEditorRow *row;
+
+ g_assert (IDE_IS_ENVIRONMENT_EDITOR (self));
+ g_assert (IDE_IS_ENVIRONMENT_VARIABLE (variable));
+
+ row = g_object_new (IDE_TYPE_ENVIRONMENT_EDITOR_ROW,
+ "variable", variable,
+ "visible", TRUE,
+ NULL);
+
+ g_signal_connect_object (row,
+ "delete",
+ G_CALLBACK (ide_environment_editor_delete_row),
+ self,
+ G_CONNECT_SWAPPED);
+
+ return GTK_WIDGET (row);
+}
+
+static void
+ide_environment_editor_disconnect (IdeEnvironmentEditor *self)
+{
+ g_assert (IDE_IS_ENVIRONMENT_EDITOR (self));
+ g_assert (IDE_IS_ENVIRONMENT (self->environment));
+
+ gtk_list_box_bind_model (GTK_LIST_BOX (self), NULL, NULL, NULL, NULL);
+
+ g_clear_object (&self->dummy);
+}
+
+static void
+ide_environment_editor_connect (IdeEnvironmentEditor *self)
+{
+ g_assert (IDE_IS_ENVIRONMENT_EDITOR (self));
+ g_assert (IDE_IS_ENVIRONMENT (self->environment));
+
+ gtk_list_box_bind_model (GTK_LIST_BOX (self),
+ G_LIST_MODEL (self->environment),
+ ide_environment_editor_create_row, self, NULL);
+
+ self->dummy_row = ide_environment_editor_create_dummy_row (self);
+ gtk_container_add (GTK_CONTAINER (self), self->dummy_row);
+}
+
+static void
+find_row_cb (GtkWidget *widget,
+ gpointer data)
+{
+ struct {
+ IdeEnvironmentVariable *variable;
+ IdeEnvironmentEditorRow *row;
+ } *lookup = data;
+
+ g_assert (lookup != NULL);
+ g_assert (GTK_IS_LIST_BOX_ROW (widget));
+
+ if (IDE_IS_ENVIRONMENT_EDITOR_ROW (widget))
+ {
+ IdeEnvironmentVariable *variable;
+
+ variable = ide_environment_editor_row_get_variable (IDE_ENVIRONMENT_EDITOR_ROW (widget));
+
+ if (variable == lookup->variable)
+ lookup->row = IDE_ENVIRONMENT_EDITOR_ROW (widget);
+ }
+}
+
+static IdeEnvironmentEditorRow *
+find_row (IdeEnvironmentEditor *self,
+ IdeEnvironmentVariable *variable)
+{
+ struct {
+ IdeEnvironmentVariable *variable;
+ IdeEnvironmentEditorRow *row;
+ } lookup = { variable, NULL };
+
+ g_assert (IDE_IS_ENVIRONMENT_EDITOR (self));
+ g_assert (IDE_IS_ENVIRONMENT_VARIABLE (variable));
+
+ gtk_container_foreach (GTK_CONTAINER (self), find_row_cb, &lookup);
+
+ return lookup.row;
+}
+
+static void
+ide_environment_editor_row_activated (GtkListBox *list_box,
+ GtkListBoxRow *row)
+{
+ IdeEnvironmentEditor *self = (IdeEnvironmentEditor *)list_box;
+
+ g_assert (GTK_IS_LIST_BOX (list_box));
+ g_assert (GTK_IS_LIST_BOX_ROW (row));
+
+ if (self->environment == NULL)
+ return;
+
+ if (self->dummy_row == GTK_WIDGET (row))
+ {
+ g_autoptr(IdeEnvironmentVariable) variable = NULL;
+
+ variable = ide_environment_variable_new (NULL, NULL);
+ ide_environment_append (self->environment, variable);
+ ide_environment_editor_row_start_editing (find_row (self, variable));
+ }
+}
+
+static void
+ide_environment_editor_destroy (GtkWidget *widget)
+{
+ IdeEnvironmentEditor *self = (IdeEnvironmentEditor *)widget;
+
+ GTK_WIDGET_CLASS (ide_environment_editor_parent_class)->destroy (widget);
+
+ g_clear_object (&self->environment);
+}
+
+static void
+ide_environment_editor_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdeEnvironmentEditor *self = IDE_ENVIRONMENT_EDITOR(object);
+
+ switch (prop_id)
+ {
+ case PROP_ENVIRONMENT:
+ g_value_set_object (value, ide_environment_editor_get_environment (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ }
+}
+
+static void
+ide_environment_editor_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdeEnvironmentEditor *self = IDE_ENVIRONMENT_EDITOR(object);
+
+ switch (prop_id)
+ {
+ case PROP_ENVIRONMENT:
+ ide_environment_editor_set_environment (self, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ }
+}
+
+static void
+ide_environment_editor_class_init (IdeEnvironmentEditorClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GtkListBoxClass *list_box_class = GTK_LIST_BOX_CLASS (klass);
+
+ object_class->get_property = ide_environment_editor_get_property;
+ object_class->set_property = ide_environment_editor_set_property;
+
+ widget_class->destroy = ide_environment_editor_destroy;
+
+ list_box_class->row_activated = ide_environment_editor_row_activated;
+
+ properties [PROP_ENVIRONMENT] =
+ g_param_spec_object ("environment",
+ "Environment",
+ "Environment",
+ IDE_TYPE_ENVIRONMENT,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, LAST_PROP, properties);
+}
+
+static void
+ide_environment_editor_init (IdeEnvironmentEditor *self)
+{
+ gtk_list_box_set_selection_mode (GTK_LIST_BOX (self), GTK_SELECTION_NONE);
+}
+
+GtkWidget *
+ide_environment_editor_new (void)
+{
+ return g_object_new (IDE_TYPE_ENVIRONMENT_EDITOR, NULL);
+}
+
+void
+ide_environment_editor_set_environment (IdeEnvironmentEditor *self,
+ IdeEnvironment *environment)
+{
+ g_return_if_fail (IDE_IS_ENVIRONMENT_EDITOR (self));
+ g_return_if_fail (IDE_IS_ENVIRONMENT (environment));
+
+ if (self->environment != environment)
+ {
+ if (self->environment != NULL)
+ {
+ ide_environment_editor_disconnect (self);
+ g_clear_object (&self->environment);
+ }
+
+ if (environment != NULL)
+ {
+ self->environment = g_object_ref (environment);
+ ide_environment_editor_connect (self);
+ }
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ENVIRONMENT]);
+ }
+}
+
+/**
+ * ide_environment_editor_get_environment:
+ *
+ * Returns: (nullable) (transfer none): An #IdeEnvironment or %NULL.
+ */
+IdeEnvironment *
+ide_environment_editor_get_environment (IdeEnvironmentEditor *self)
+{
+ g_return_val_if_fail (IDE_IS_ENVIRONMENT_EDITOR (self), NULL);
+
+ return self->environment;
+}
diff --git a/src/libide/gui/ide-environment-editor.h b/src/libide/gui/ide-environment-editor.h
new file mode 100644
index 000000000..2a5731da2
--- /dev/null
+++ b/src/libide/gui/ide-environment-editor.h
@@ -0,0 +1,42 @@
+/* ide-environment-editor.h
+ *
+ * Copyright 2016-2019 Christian Hergert <christian hergert me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+#include <libide-core.h>
+#include <libide-threading.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_ENVIRONMENT_EDITOR (ide_environment_editor_get_type())
+
+IDE_AVAILABLE_IN_3_32
+G_DECLARE_FINAL_TYPE (IdeEnvironmentEditor, ide_environment_editor, IDE, ENVIRONMENT_EDITOR, GtkListBox)
+
+IDE_AVAILABLE_IN_3_32
+GtkWidget *ide_environment_editor_new (void);
+IDE_AVAILABLE_IN_3_32
+IdeEnvironment *ide_environment_editor_get_environment (IdeEnvironmentEditor *self);
+IDE_AVAILABLE_IN_3_32
+void ide_environment_editor_set_environment (IdeEnvironmentEditor *self,
+ IdeEnvironment *environment);
+
+G_END_DECLS
diff --git a/src/libide/util/ide-fancy-tree-view.c b/src/libide/gui/ide-fancy-tree-view.c
similarity index 98%
rename from src/libide/util/ide-fancy-tree-view.c
rename to src/libide/gui/ide-fancy-tree-view.c
index 83584f939..30cf656c7 100644
--- a/src/libide/util/ide-fancy-tree-view.c
+++ b/src/libide/gui/ide-fancy-tree-view.c
@@ -24,8 +24,8 @@
#include <dazzle.h>
-#include "util/ide-cell-renderer-fancy.h"
-#include "util/ide-fancy-tree-view.h"
+#include "ide-cell-renderer-fancy.h"
+#include "ide-fancy-tree-view.h"
/**
* SECTION:ide-fancy-tree-view:
diff --git a/src/libide/util/ide-fancy-tree-view.h b/src/libide/gui/ide-fancy-tree-view.h
similarity index 89%
rename from src/libide/util/ide-fancy-tree-view.h
rename to src/libide/gui/ide-fancy-tree-view.h
index 73965daa5..e8c7caf0b 100644
--- a/src/libide/util/ide-fancy-tree-view.h
+++ b/src/libide/gui/ide-fancy-tree-view.h
@@ -20,9 +20,12 @@
#pragma once
-#include <gtk/gtk.h>
+#if !defined (IDE_GUI_INSIDE) && !defined (IDE_GUI_COMPILATION)
+# error "Only <libide-gui.h> can be included directly."
+#endif
-#include "ide-version-macros.h"
+#include <gtk/gtk.h>
+#include <libide-core.h>
G_BEGIN_DECLS
@@ -36,10 +39,7 @@ struct _IdeFancyTreeViewClass
GtkTreeViewClass parent_class;
/*< private >*/
- gpointer _reserved1;
- gpointer _reserved2;
- gpointer _reserved3;
- gpointer _reserved4;
+ gpointer _reserved[8];
};
IDE_AVAILABLE_IN_3_32
diff --git a/src/libide/gui/ide-frame-actions.c b/src/libide/gui/ide-frame-actions.c
new file mode 100644
index 000000000..ea2af0f3e
--- /dev/null
+++ b/src/libide/gui/ide-frame-actions.c
@@ -0,0 +1,429 @@
+/* ide-frame-actions.c
+ *
+ * Copyright 2017-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-frame-actions"
+
+#include "config.h"
+
+#include "ide-frame.h"
+#include "ide-gui-global.h"
+#include "ide-gui-private.h"
+#include "ide-workbench.h"
+
+static void
+ide_frame_actions_next_page (GSimpleAction *action,
+ GVariant *variant,
+ gpointer user_data)
+{
+ IdeFrame *self = user_data;
+
+ g_assert (IDE_IS_FRAME (self));
+
+ g_signal_emit_by_name (self, "change-current-page", 1);
+}
+
+static void
+ide_frame_actions_previous_page (GSimpleAction *action,
+ GVariant *variant,
+ gpointer user_data)
+{
+ IdeFrame *self = user_data;
+
+ g_assert (IDE_IS_FRAME (self));
+
+ g_signal_emit_by_name (self, "change-current-page", -1);
+}
+
+static void
+ide_frame_actions_close_page (GSimpleAction *action,
+ GVariant *variant,
+ gpointer user_data)
+{
+ IdeFrame *self = user_data;
+ IdePage *page;
+
+ g_assert (IDE_IS_FRAME (self));
+
+ page = ide_frame_get_visible_child (self);
+ if (page != NULL)
+ _ide_frame_request_close (self, page);
+}
+
+static void
+ide_frame_actions_move (IdeFrame *self,
+ gint direction)
+{
+ IdePage *page;
+ IdeFrame *dest;
+ GtkWidget *grid;
+ GtkWidget *column;
+ gint index = 0;
+
+ g_assert (IDE_IS_FRAME (self));
+ g_assert (direction == 1 || direction == -1);
+
+ page = ide_frame_get_visible_child (self);
+
+ g_return_if_fail (page != NULL);
+ g_return_if_fail (IDE_IS_PAGE (page));
+
+ grid = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_GRID);
+ g_return_if_fail (grid != NULL);
+ g_return_if_fail (IDE_IS_GRID (grid));
+
+ column = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_GRID_COLUMN);
+ g_return_if_fail (column != NULL);
+ g_return_if_fail (IDE_IS_GRID_COLUMN (column));
+
+ gtk_container_child_get (GTK_CONTAINER (grid), GTK_WIDGET (column),
+ "index", &index,
+ NULL);
+
+ dest = _ide_grid_get_nth_stack (IDE_GRID (grid), index + direction);
+
+ g_return_if_fail (dest != NULL);
+ g_return_if_fail (dest != self);
+ g_return_if_fail (IDE_IS_FRAME (dest));
+
+ _ide_frame_transfer (self, dest, page);
+}
+
+static void
+ide_frame_actions_move_right (GSimpleAction *action,
+ GVariant *variant,
+ gpointer user_data)
+{
+ IdeFrame *self = user_data;
+
+ g_assert (G_IS_SIMPLE_ACTION (action));
+ g_assert (IDE_IS_FRAME (self));
+
+ ide_frame_actions_move (self, 1);
+}
+
+static void
+ide_frame_actions_move_left (GSimpleAction *action,
+ GVariant *variant,
+ gpointer user_data)
+{
+ IdeFrame *self = user_data;
+
+ g_assert (G_IS_SIMPLE_ACTION (action));
+ g_assert (IDE_IS_FRAME (self));
+
+ ide_frame_actions_move (self, -1);
+}
+
+static void
+ide_frame_actions_split_page (GSimpleAction *action,
+ GVariant *variant,
+ gpointer user_data)
+{
+ IdeFrame *self = user_data;
+ g_autoptr(GFile) file = NULL;
+ IdeBufferManager *bufmgr;
+ GObjectClass *klass;
+ const gchar *path;
+ GParamSpec *pspec;
+ IdeContext *context;
+ IdeBuffer *buffer;
+ GtkWidget *column;
+ GtkWidget *grid;
+ IdeFrame *dest;
+ IdePage *page;
+ IdePage *split_page;
+ gint index = 0;
+
+ g_assert (G_IS_SIMPLE_ACTION (action));
+ g_assert (IDE_IS_FRAME (self));
+ g_assert (g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING));
+
+ column = gtk_widget_get_parent (GTK_WIDGET (self));
+
+ if (column == NULL || !IDE_IS_GRID_COLUMN (column))
+ {
+ g_warning ("Failed to locate ancestor grid column");
+ return;
+ }
+
+ if (!(grid = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_GRID)))
+ {
+ g_warning ("Failed to locate ancestor grid");
+ return;
+ }
+
+ if (!(page = ide_frame_get_visible_child (self)))
+ {
+ g_warning ("No page available to split");
+ return;
+ }
+
+ if ((path = g_variant_get_string (variant, NULL)) &&
+ !ide_str_empty0 (path) &&
+ (context = ide_widget_get_context (GTK_WIDGET (self))) &&
+ (bufmgr = ide_buffer_manager_from_context (context)) &&
+ (file = g_file_new_for_path (path)) &&
+ (buffer = ide_buffer_manager_find_buffer (bufmgr, file)) &&
+ (klass = G_OBJECT_GET_CLASS (page)) &&
+ (pspec = g_object_class_find_property (klass, "buffer")) &&
+ g_type_is_a (pspec->value_type, IDE_TYPE_BUFFER))
+ {
+ split_page = g_object_new (G_OBJECT_TYPE (page),
+ "buffer", buffer,
+ "visible", TRUE,
+ NULL);
+ }
+ else
+ {
+ if (!ide_page_get_can_split (page))
+ {
+ g_warning ("Attempt to split a page that cannot be split");
+ return;
+ }
+
+ if (!(split_page = ide_page_create_split (page)))
+ {
+ g_warning ("%s failed to create a split",
+ G_OBJECT_TYPE_NAME (page));
+ return;
+ }
+ }
+
+ g_assert (IDE_IS_PAGE (split_page));
+ g_assert (IDE_IS_GRID_COLUMN (column));
+
+ gtk_container_child_get (GTK_CONTAINER (column), GTK_WIDGET (self),
+ "index", &index,
+ NULL);
+
+ dest = _ide_grid_get_nth_stack_for_column (IDE_GRID (grid),
+ IDE_GRID_COLUMN (column),
+ ++index);
+
+ g_assert (IDE_IS_FRAME (dest));
+
+ gtk_container_add (GTK_CONTAINER (dest), GTK_WIDGET (split_page));
+}
+
+static void
+ide_frame_actions_open_in_new_frame (GSimpleAction *action,
+ GVariant *variant,
+ gpointer user_data)
+{
+ IdeFrame *self = user_data;
+ const gchar *filepath;
+ GtkWidget *grid;
+ GtkWidget *column;
+ IdeFrame *dest;
+ IdePage *page;
+ gint index = 0;
+
+ g_assert (G_IS_SIMPLE_ACTION (action));
+ g_assert (IDE_IS_FRAME (self));
+ g_assert (g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING));
+
+ filepath = g_variant_get_string (variant, NULL);
+ page = ide_frame_get_visible_child (self);
+
+ g_return_if_fail (page != NULL);
+ g_return_if_fail (IDE_IS_PAGE (page));
+
+ if (!ide_str_empty0 (filepath))
+ {
+ g_autoptr (GFile) file = NULL;
+ IdeBufferManager *buffer_manager;
+ IdeContext *context;
+ IdeBuffer *buffer;
+
+ context = ide_widget_get_context (GTK_WIDGET (self));
+ buffer_manager = ide_buffer_manager_from_context (context);
+ file = g_file_new_for_path (filepath);
+
+ if ((buffer = ide_buffer_manager_find_buffer (buffer_manager, file)))
+ page = g_object_new (G_OBJECT_TYPE (page),
+ "buffer", buffer,
+ "visible", TRUE,
+ NULL);
+ else
+ return;
+ }
+ else
+ {
+ g_return_if_fail (ide_page_get_can_split (page));
+
+ page = ide_page_create_split (page);
+ }
+
+ if (page == NULL)
+ {
+ g_warning ("Requested split page but NULL was returned");
+ return;
+ }
+
+ grid = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_GRID);
+
+ g_return_if_fail (grid != NULL);
+ g_return_if_fail (IDE_IS_GRID (grid));
+
+ column = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_GRID_COLUMN);
+
+ g_return_if_fail (column != NULL);
+ g_return_if_fail (IDE_IS_GRID_COLUMN (column));
+
+ gtk_container_child_get (GTK_CONTAINER (grid), GTK_WIDGET (column),
+ "index", &index,
+ NULL);
+
+ dest = _ide_grid_get_nth_stack (IDE_GRID (grid), ++index);
+
+ g_return_if_fail (dest != NULL);
+ g_return_if_fail (IDE_IS_FRAME (dest));
+
+ gtk_container_add (GTK_CONTAINER (dest), GTK_WIDGET (page));
+}
+
+static void
+ide_frame_actions_close_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeFrame *self = (IdeFrame *)object;
+ GtkWidget *parent;
+
+ g_assert (IDE_IS_FRAME (self));
+ g_assert (G_IS_ASYNC_RESULT (result));
+
+ if (!ide_frame_agree_to_close_finish (self, result, NULL))
+ return;
+
+ /* Things might have changed during the async op */
+ parent = gtk_widget_get_parent (GTK_WIDGET (self));
+ if (!IDE_IS_GRID_COLUMN (parent))
+ return;
+
+ /* Make sure there is still more than a single stack */
+ if (dzl_multi_paned_get_n_children (DZL_MULTI_PANED (parent)) > 1)
+ gtk_widget_destroy (GTK_WIDGET (self));
+}
+
+static void
+ide_frame_actions_close_stack (GSimpleAction *action,
+ GVariant *variant,
+ gpointer user_data)
+{
+ IdeFrame *self = user_data;
+
+ g_assert (G_IS_SIMPLE_ACTION (action));
+ g_assert (IDE_IS_FRAME (self));
+
+ ide_frame_agree_to_close_async (self,
+ NULL,
+ ide_frame_actions_close_cb,
+ NULL);
+}
+
+static void
+ide_frame_actions_show_list (GSimpleAction *action,
+ GVariant *variant,
+ gpointer user_data)
+{
+ IdeFrame *self = user_data;
+ IdeFrameHeader *header;
+
+ g_assert (G_IS_SIMPLE_ACTION (action));
+ g_assert (IDE_IS_FRAME (self));
+
+ header = IDE_FRAME_HEADER (ide_frame_get_titlebar (self));
+ _ide_frame_header_focus_list (header);
+}
+
+static const GActionEntry actions[] = {
+ { "open-in-new-frame", ide_frame_actions_open_in_new_frame, "s" },
+ { "close-stack", ide_frame_actions_close_stack },
+ { "close-page", ide_frame_actions_close_page },
+ { "next-page", ide_frame_actions_next_page },
+ { "previous-page", ide_frame_actions_previous_page },
+ { "move-right", ide_frame_actions_move_right },
+ { "move-left", ide_frame_actions_move_left },
+ { "split-page", ide_frame_actions_split_page, "s" },
+ { "show-list", ide_frame_actions_show_list },
+};
+
+void
+_ide_frame_update_actions (IdeFrame *self)
+{
+ IdePage *page;
+ GtkWidget *parent;
+ gboolean has_page = FALSE;
+ gboolean can_split_page = FALSE;
+ gboolean can_close_stack = FALSE;
+
+ g_return_if_fail (IDE_IS_FRAME (self));
+
+ page = ide_frame_get_visible_child (self);
+
+ if (page != NULL)
+ {
+ has_page = TRUE;
+ can_split_page = ide_page_get_can_split (page);
+ }
+
+ /* If there is more than one stack in the column, then we can close
+ * this stack directly without involving the column.
+ */
+ parent = gtk_widget_get_parent (GTK_WIDGET (self));
+ if (IDE_IS_GRID_COLUMN (parent))
+ can_close_stack = dzl_multi_paned_get_n_children (DZL_MULTI_PANED (parent)) > 1;
+
+ dzl_gtk_widget_action_set (GTK_WIDGET (self), "frame", "move-right",
+ "enabled", has_page,
+ NULL);
+ dzl_gtk_widget_action_set (GTK_WIDGET (self), "frame", "move-left",
+ "enabled", has_page,
+ NULL);
+ dzl_gtk_widget_action_set (GTK_WIDGET (self), "frame", "open-in-new-frame",
+ "enabled", can_split_page,
+ NULL);
+ dzl_gtk_widget_action_set (GTK_WIDGET (self), "frame", "split-page",
+ "enabled", can_split_page,
+ NULL);
+ dzl_gtk_widget_action_set (GTK_WIDGET (self), "frame", "close-stack",
+ "enabled", can_close_stack,
+ NULL);
+}
+
+void
+_ide_frame_init_actions (IdeFrame *self)
+{
+ g_autoptr(GSimpleActionGroup) group = NULL;
+
+ g_return_if_fail (IDE_IS_FRAME (self));
+
+ group = g_simple_action_group_new ();
+ g_action_map_add_action_entries (G_ACTION_MAP (group),
+ actions,
+ G_N_ELEMENTS (actions),
+ self);
+ gtk_widget_insert_action_group (GTK_WIDGET (self),
+ "frame",
+ G_ACTION_GROUP (group));
+
+ _ide_frame_update_actions (self);
+}
diff --git a/src/libide/gui/ide-frame-addin.c b/src/libide/gui/ide-frame-addin.c
new file mode 100644
index 000000000..81893a80a
--- /dev/null
+++ b/src/libide/gui/ide-frame-addin.c
@@ -0,0 +1,111 @@
+/* ide-frame-addin.c
+ *
+ * Copyright 2017-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-frame-addin"
+
+#include "config.h"
+
+#include "ide-frame-addin.h"
+
+/**
+ * SECTION:ide-frame-addin
+ * @title: IdeFrameAddin
+ * @short_description: addins created for every #IdeFrame
+ *
+ * Since: 3.32
+ */
+
+G_DEFINE_INTERFACE (IdeFrameAddin, ide_frame_addin, G_TYPE_OBJECT)
+
+static void
+ide_frame_addin_default_init (IdeFrameAddinInterface *iface)
+{
+}
+
+/**
+ * ide_frame_addin_load:
+ * @self: An #IdeFrameAddin
+ * @frame: An #IdeFrame
+ *
+ * This function should be implemented by #IdeFrameAddin plugins
+ * in #IdeFrameAddinInterface.
+ *
+ * This virtual method is called when the plugin should load itself.
+ * A new instance of the plugin is created for every #IdeFrame
+ * that is created in Builder.
+ *
+ * Since: 3.32
+ */
+void
+ide_frame_addin_load (IdeFrameAddin *self,
+ IdeFrame *frame)
+{
+ g_return_if_fail (IDE_IS_FRAME_ADDIN (self));
+ g_return_if_fail (IDE_IS_FRAME (frame));
+
+ if (IDE_FRAME_ADDIN_GET_IFACE (self)->load)
+ IDE_FRAME_ADDIN_GET_IFACE (self)->load (self, frame);
+}
+
+/**
+ * ide_frame_addin_unload:
+ * @self: An #IdeFrameAddin
+ * @frame: An #IdeFrame
+ *
+ * This function should be implemented by #IdeFrameAddin plugins
+ * in #IdeFrameAddinInterface.
+ *
+ * This virtual method is called when the plugin should unload itself.
+ * It should revert anything performed via ide_frame_addin_load().
+ *
+ * Since: 3.32
+ */
+void
+ide_frame_addin_unload (IdeFrameAddin *self,
+ IdeFrame *frame)
+{
+ g_return_if_fail (IDE_IS_FRAME_ADDIN (self));
+ g_return_if_fail (IDE_IS_FRAME (frame));
+
+ if (IDE_FRAME_ADDIN_GET_IFACE (self)->unload)
+ IDE_FRAME_ADDIN_GET_IFACE (self)->unload (self, frame);
+}
+
+/**
+ * ide_frame_addin_set_page:
+ * @self: an #IdeFrameAddin
+ * @page: (nullable): An #IdePage or %NULL.
+ *
+ * This virtual method is called whenever the active page changes
+ * in the #IdePage. Plugins may want to alter what controls
+ * are displayed on the frame based on the current page.
+ *
+ * Since: 3.32
+ */
+void
+ide_frame_addin_set_page (IdeFrameAddin *self,
+ IdePage *page)
+{
+ g_return_if_fail (IDE_IS_FRAME_ADDIN (self));
+ g_return_if_fail (!page || IDE_IS_PAGE (page));
+
+ if (IDE_FRAME_ADDIN_GET_IFACE (self)->set_page)
+ IDE_FRAME_ADDIN_GET_IFACE (self)->set_page (self, page);
+}
diff --git a/src/libide/gui/ide-frame-addin.h b/src/libide/gui/ide-frame-addin.h
new file mode 100644
index 000000000..819b9c551
--- /dev/null
+++ b/src/libide/gui/ide-frame-addin.h
@@ -0,0 +1,65 @@
+/* ide-frame-addin.h
+ *
+ * Copyright 2017-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#if !defined (IDE_GUI_INSIDE) && !defined (IDE_GUI_COMPILATION)
+# error "Only <libide-gui.h> can be included directly."
+#endif
+
+#include <gtk/gtk.h>
+#include <libide-core.h>
+
+#include "ide-frame.h"
+#include "ide-page.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_FRAME_ADDIN (ide_frame_addin_get_type())
+
+IDE_AVAILABLE_IN_3_32
+G_DECLARE_INTERFACE (IdeFrameAddin, ide_frame_addin, IDE, FRAME_ADDIN, GObject)
+
+struct _IdeFrameAddinInterface
+{
+ GTypeInterface parent_iface;
+
+ void (*load) (IdeFrameAddin *self,
+ IdeFrame *frame);
+ void (*unload) (IdeFrameAddin *self,
+ IdeFrame *frame);
+ void (*set_page) (IdeFrameAddin *self,
+ IdePage *page);
+};
+
+IDE_AVAILABLE_IN_3_32
+void ide_frame_addin_load (IdeFrameAddin *self,
+ IdeFrame *frame);
+IDE_AVAILABLE_IN_3_32
+void ide_frame_addin_unload (IdeFrameAddin *self,
+ IdeFrame *frame);
+IDE_AVAILABLE_IN_3_32
+void ide_frame_addin_set_page (IdeFrameAddin *self,
+ IdePage *page);
+IDE_AVAILABLE_IN_3_32
+IdeFrameAddin *ide_frame_addin_find_by_module_name (IdeFrame *frame,
+ const gchar *module_name);
+
+G_END_DECLS
diff --git a/src/libide/layout/ide-layout-stack-header.c b/src/libide/gui/ide-frame-header.c
similarity index 72%
rename from src/libide/layout/ide-layout-stack-header.c
rename to src/libide/gui/ide-frame-header.c
index 06f39c81f..9943fd10e 100644
--- a/src/libide/layout/ide-layout-stack-header.c
+++ b/src/libide/gui/ide-frame-header.c
@@ -1,6 +1,6 @@
-/* ide-layout-stack-header.c
+/* ide-frame-header.c
*
- * Copyright 2017-2019 Christian Hergert <chergert redhat com>
+ * Copyright 2016-2019 Christian Hergert <chergert redhat com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,23 +18,23 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
-#define G_LOG_DOMAIN "ide-layout-stack-header"
+#define G_LOG_DOMAIN "ide-frame-header"
#include "config.h"
#include <glib/gi18n.h>
-#include "layout/ide-layout-private.h"
-#include "layout/ide-layout-stack-header.h"
+#include "ide-gui-private.h"
+#include "ide-frame-header.h"
#define CSS_PROVIDER_PRIORITY (GTK_STYLE_PROVIDER_PRIORITY_APPLICATION + 100)
/**
- * SECTION:ide-layout-stack-header
- * @title: IdeLayoutStackHeader
+ * SECTION:ide-frame-header
+ * @title: IdeFrameHeader
* @short_description: The header above document stacks
*
- * The IdeLayoutStackHeader is the titlebar widget above stacks of documents.
+ * The IdeFrameHeader is the titlebar widget above stacks of documents.
* It is used to add state when a given document is in view.
*
* It can also track the primary color of the content and update it's
@@ -43,7 +43,7 @@
* Since: 3.32
*/
-struct _IdeLayoutStackHeader
+struct _IdeFrameHeader
{
DzlPriorityBox parent_instance;
@@ -79,27 +79,27 @@ enum {
N_PROPS
};
-G_DEFINE_TYPE (IdeLayoutStackHeader, ide_layout_stack_header, DZL_TYPE_PRIORITY_BOX)
+G_DEFINE_TYPE (IdeFrameHeader, ide_frame_header, DZL_TYPE_PRIORITY_BOX)
static GParamSpec *properties [N_PROPS];
void
-_ide_layout_stack_header_focus_list (IdeLayoutStackHeader *self)
+_ide_frame_header_focus_list (IdeFrameHeader *self)
{
- g_return_if_fail (IDE_IS_LAYOUT_STACK_HEADER (self));
+ g_return_if_fail (IDE_IS_FRAME_HEADER (self));
gtk_popover_popup (self->title_popover);
gtk_widget_grab_focus (GTK_WIDGET (self->title_list_box));
}
void
-_ide_layout_stack_header_hide (IdeLayoutStackHeader *self)
+_ide_frame_header_hide (IdeFrameHeader *self)
{
GtkPopover *popover;
- g_return_if_fail (IDE_IS_LAYOUT_STACK_HEADER (self));
+ g_return_if_fail (IDE_IS_FRAME_HEADER (self));
- /* This is like _ide_layout_stack_header_popdown() but we hide the
+ /* This is like _ide_frame_header_popdown() but we hide the
* popovers immediately without performing the popdown animation.
*/
@@ -111,11 +111,11 @@ _ide_layout_stack_header_hide (IdeLayoutStackHeader *self)
}
void
-_ide_layout_stack_header_popdown (IdeLayoutStackHeader *self)
+_ide_frame_header_popdown (IdeFrameHeader *self)
{
GtkPopover *popover;
- g_return_if_fail (IDE_IS_LAYOUT_STACK_HEADER (self));
+ g_return_if_fail (IDE_IS_FRAME_HEADER (self));
popover = gtk_menu_button_get_popover (GTK_MENU_BUTTON (self->document_button));
if (popover != NULL)
@@ -125,13 +125,13 @@ _ide_layout_stack_header_popdown (IdeLayoutStackHeader *self)
}
void
-_ide_layout_stack_header_update (IdeLayoutStackHeader *self,
- IdeLayoutView *view)
+_ide_frame_header_update (IdeFrameHeader *self,
+ IdePage *view)
{
- const gchar *action = "layoutstack.close-view";
+ const gchar *action = "frame.close-page";
- g_assert (IDE_IS_LAYOUT_STACK_HEADER (self));
- g_assert (!view || IDE_IS_LAYOUT_VIEW (view));
+ g_assert (IDE_IS_FRAME_HEADER (self));
+ g_assert (!view || IDE_IS_PAGE (view));
/*
* Update our menus for the document to include the menu type needed for the
@@ -144,7 +144,7 @@ _ide_layout_stack_header_update (IdeLayoutStackHeader *self,
if (view != NULL)
{
- const gchar *menu_id = ide_layout_view_get_menu_id (view);
+ const gchar *menu_id = ide_page_get_menu_id (view);
if (menu_id != NULL)
{
@@ -173,14 +173,14 @@ _ide_layout_stack_header_update (IdeLayoutStackHeader *self,
GtkWidget *stack;
GtkWidget *column;
- action = "layoutgridcolumn.close";
- stack = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_LAYOUT_STACK);
- column = gtk_widget_get_ancestor (GTK_WIDGET (stack), IDE_TYPE_LAYOUT_GRID_COLUMN);
+ action = "gridcolumn.close";
+ stack = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_FRAME);
+ column = gtk_widget_get_ancestor (GTK_WIDGET (stack), IDE_TYPE_GRID_COLUMN);
if (stack != NULL && column != NULL)
{
if (dzl_multi_paned_get_n_children (DZL_MULTI_PANED (column)) > 1)
- action = "layoutstack.close-stack";
+ action = "frame.close-stack";
}
}
@@ -192,48 +192,48 @@ _ide_layout_stack_header_update (IdeLayoutStackHeader *self,
* (inidicated by NULL view).
*/
if (view == NULL)
- _ide_layout_stack_header_popdown (self);
+ _ide_frame_header_popdown (self);
}
static void
close_view_cb (GtkButton *button,
- IdeLayoutStackHeader *self)
+ IdeFrameHeader *self)
{
GtkWidget *stack;
GtkWidget *row;
GtkWidget *view;
g_assert (GTK_IS_BUTTON (button));
- g_assert (IDE_IS_LAYOUT_STACK_HEADER (self));
+ g_assert (IDE_IS_FRAME_HEADER (self));
row = gtk_widget_get_ancestor (GTK_WIDGET (button), GTK_TYPE_LIST_BOX_ROW);
if (row == NULL)
return;
- view = g_object_get_data (G_OBJECT (row), "IDE_LAYOUT_VIEW");
+ view = g_object_get_data (G_OBJECT (row), "IDE_PAGE");
if (view == NULL)
return;
- stack = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_LAYOUT_STACK);
+ stack = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_FRAME);
if (stack == NULL)
return;
- _ide_layout_stack_request_close (IDE_LAYOUT_STACK (stack), IDE_LAYOUT_VIEW (view));
+ _ide_frame_request_close (IDE_FRAME (stack), IDE_PAGE (view));
}
static GtkWidget *
create_document_row (gpointer item,
gpointer user_data)
{
- IdeLayoutStackHeader *self = user_data;
+ IdeFrameHeader *self = user_data;
GtkListBoxRow *row;
GtkButton *close_button;
GtkLabel *label;
GtkImage *image;
GtkBox *box;
- g_assert (IDE_IS_LAYOUT_VIEW (item));
- g_assert (IDE_IS_LAYOUT_STACK_HEADER (self));
+ g_assert (IDE_IS_PAGE (item));
+ g_assert (IDE_IS_FRAME_HEADER (self));
row = g_object_new (GTK_TYPE_LIST_BOX_ROW,
"visible", TRUE,
@@ -266,7 +266,7 @@ create_document_row (gpointer item,
g_object_bind_property (item, "icon-name", image, "icon-name", G_BINDING_SYNC_CREATE);
g_object_bind_property (item, "modified", label, "bold", G_BINDING_SYNC_CREATE);
g_object_bind_property (item, "title", label, "label", G_BINDING_SYNC_CREATE);
- g_object_set_data (G_OBJECT (row), "IDE_LAYOUT_VIEW", item);
+ g_object_set_data (G_OBJECT (row), "IDE_PAGE", item);
gtk_container_add (GTK_CONTAINER (row), GTK_WIDGET (box));
gtk_container_add (GTK_CONTAINER (box), GTK_WIDGET (image));
@@ -277,10 +277,10 @@ create_document_row (gpointer item,
}
void
-_ide_layout_stack_header_set_views (IdeLayoutStackHeader *self,
+_ide_frame_header_set_pages (IdeFrameHeader *self,
GListModel *model)
{
- g_assert (IDE_IS_LAYOUT_STACK_HEADER (self));
+ g_assert (IDE_IS_FRAME_HEADER (self));
g_assert (!model || G_IS_LIST_MODEL (model));
gtk_list_box_bind_model (self->title_list_box,
@@ -290,37 +290,36 @@ _ide_layout_stack_header_set_views (IdeLayoutStackHeader *self,
}
static void
-ide_layout_stack_header_view_row_activated (GtkListBox *list_box,
+ide_frame_header_view_row_activated (GtkListBox *list_box,
GtkListBoxRow *row,
- IdeLayoutStackHeader *self)
+ IdeFrameHeader *self)
{
GtkWidget *stack;
- GtkWidget *view;
+ GtkWidget *page;
g_assert (GTK_IS_LIST_BOX (list_box));
g_assert (GTK_IS_LIST_BOX_ROW (row));
- g_assert (IDE_IS_LAYOUT_STACK_HEADER (self));
+ g_assert (IDE_IS_FRAME_HEADER (self));
- stack = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_LAYOUT_STACK);
- view = g_object_get_data (G_OBJECT (row), "IDE_LAYOUT_VIEW");
+ stack = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_FRAME);
+ page = g_object_get_data (G_OBJECT (row), "IDE_PAGE");
- if (stack != NULL && view != NULL)
+ if (stack != NULL && page != NULL)
{
- ide_layout_stack_set_visible_child (IDE_LAYOUT_STACK (stack),
- IDE_LAYOUT_VIEW (view));
- gtk_widget_grab_focus (view);
+ ide_frame_set_visible_child (IDE_FRAME (stack), IDE_PAGE (page));
+ gtk_widget_grab_focus (page);
}
- _ide_layout_stack_header_popdown (self);
+ _ide_frame_header_popdown (self);
}
static gboolean
-ide_layout_stack_header_update_css (IdeLayoutStackHeader *self)
+ide_frame_header_update_css (IdeFrameHeader *self)
{
g_autoptr(GString) str = NULL;
g_autoptr(GError) error = NULL;
- g_assert (IDE_IS_LAYOUT_STACK_HEADER (self));
+ g_assert (IDE_IS_FRAME_HEADER (self));
g_assert (self->css_provider != NULL);
g_assert (GTK_IS_CSS_PROVIDER (self->css_provider));
@@ -336,7 +335,7 @@ ide_layout_stack_header_update_css (IdeLayoutStackHeader *self)
{
g_autofree gchar *bgstr = gdk_rgba_to_string (&self->background_rgba);
- g_string_append (str, "idelayoutstackheader {\n");
+ g_string_append (str, "ideframeheader {\n");
g_string_append (str, " background: none;\n");
g_string_append_printf (str, " background-color: %s;\n", bgstr);
g_string_append (str, " transition: background-color 400ms;\n");
@@ -375,26 +374,26 @@ ide_layout_stack_header_update_css (IdeLayoutStackHeader *self)
}
static void
-ide_layout_stack_header_queue_update_css (IdeLayoutStackHeader *self)
+ide_frame_header_queue_update_css (IdeFrameHeader *self)
{
- g_assert (IDE_IS_LAYOUT_STACK_HEADER (self));
+ g_assert (IDE_IS_FRAME_HEADER (self));
if (self->update_css_handler == 0)
self->update_css_handler =
gdk_threads_add_idle_full (G_PRIORITY_HIGH,
- (GSourceFunc) ide_layout_stack_header_update_css,
+ (GSourceFunc) ide_frame_header_update_css,
g_object_ref (self),
g_object_unref);
}
void
-_ide_layout_stack_header_set_background_rgba (IdeLayoutStackHeader *self,
+_ide_frame_header_set_background_rgba (IdeFrameHeader *self,
const GdkRGBA *background_rgba)
{
GdkRGBA old;
gboolean old_set;
- g_assert (IDE_IS_LAYOUT_STACK_HEADER (self));
+ g_assert (IDE_IS_FRAME_HEADER (self));
old_set = self->background_rgba_set;
old = self->background_rgba;
@@ -405,17 +404,17 @@ _ide_layout_stack_header_set_background_rgba (IdeLayoutStackHeader *self,
self->background_rgba_set = !!background_rgba;
if (self->background_rgba_set != old_set || !gdk_rgba_equal (&self->background_rgba, &old))
- ide_layout_stack_header_queue_update_css (self);
+ ide_frame_header_queue_update_css (self);
}
void
-_ide_layout_stack_header_set_foreground_rgba (IdeLayoutStackHeader *self,
+_ide_frame_header_set_foreground_rgba (IdeFrameHeader *self,
const GdkRGBA *foreground_rgba)
{
GdkRGBA old;
gboolean old_set;
- g_assert (IDE_IS_LAYOUT_STACK_HEADER (self));
+ g_assert (IDE_IS_FRAME_HEADER (self));
old_set = self->foreground_rgba_set;
old = self->foreground_rgba;
@@ -426,14 +425,14 @@ _ide_layout_stack_header_set_foreground_rgba (IdeLayoutStackHeader *self,
self->foreground_rgba_set = !!foreground_rgba;
if (self->background_rgba_set != old_set || !gdk_rgba_equal (&self->foreground_rgba, &old))
- ide_layout_stack_header_queue_update_css (self);
+ ide_frame_header_queue_update_css (self);
}
static void
update_widget_providers (GtkWidget *widget,
- IdeLayoutStackHeader *self)
+ IdeFrameHeader *self)
{
- g_assert (IDE_IS_LAYOUT_STACK_HEADER (self));
+ g_assert (IDE_IS_FRAME_HEADER (self));
g_assert (GTK_IS_WIDGET (widget));
/*
@@ -464,29 +463,29 @@ update_widget_providers (GtkWidget *widget,
}
static void
-ide_layout_stack_header_add (GtkContainer *container,
+ide_frame_header_add (GtkContainer *container,
GtkWidget *widget)
{
- IdeLayoutStackHeader *self = (IdeLayoutStackHeader *)container;
+ IdeFrameHeader *self = (IdeFrameHeader *)container;
- g_assert (IDE_IS_LAYOUT_STACK_HEADER (self));
+ g_assert (IDE_IS_FRAME_HEADER (self));
g_assert (GTK_IS_WIDGET (widget));
- GTK_CONTAINER_CLASS (ide_layout_stack_header_parent_class)->add (container, widget);
+ GTK_CONTAINER_CLASS (ide_frame_header_parent_class)->add (container, widget);
update_widget_providers (widget, self);
}
static void
-ide_layout_stack_header_get_preferred_width (GtkWidget *widget,
+ide_frame_header_get_preferred_width (GtkWidget *widget,
gint *min_width,
gint *nat_width)
{
- g_assert (IDE_IS_LAYOUT_STACK_HEADER (widget));
+ g_assert (IDE_IS_FRAME_HEADER (widget));
g_assert (min_width != NULL);
g_assert (nat_width != NULL);
- GTK_WIDGET_CLASS (ide_layout_stack_header_parent_class)->get_preferred_width (widget, min_width,
nat_width);
+ GTK_WIDGET_CLASS (ide_frame_header_parent_class)->get_preferred_width (widget, min_width, nat_width);
/*
* We don't want changes to the natural width to influence our positioning of
@@ -497,11 +496,11 @@ ide_layout_stack_header_get_preferred_width (GtkWidget *widget,
}
static void
-ide_layout_stack_header_destroy (GtkWidget *widget)
+ide_frame_header_destroy (GtkWidget *widget)
{
- IdeLayoutStackHeader *self = (IdeLayoutStackHeader *)widget;
+ IdeFrameHeader *self = (IdeFrameHeader *)widget;
- g_assert (IDE_IS_LAYOUT_STACK_HEADER (self));
+ g_assert (IDE_IS_FRAME_HEADER (self));
dzl_clear_source (&self->update_css_handler);
g_clear_object (&self->css_provider);
@@ -511,16 +510,16 @@ ide_layout_stack_header_destroy (GtkWidget *widget)
g_clear_object (&self->menu);
- GTK_WIDGET_CLASS (ide_layout_stack_header_parent_class)->destroy (widget);
+ GTK_WIDGET_CLASS (ide_frame_header_parent_class)->destroy (widget);
}
static void
-ide_layout_stack_header_get_property (GObject *object,
+ide_frame_header_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
- IdeLayoutStackHeader *self = IDE_LAYOUT_STACK_HEADER (object);
+ IdeFrameHeader *self = IDE_FRAME_HEADER (object);
switch (prop_id)
{
@@ -542,25 +541,25 @@ ide_layout_stack_header_get_property (GObject *object,
}
static void
-ide_layout_stack_header_set_property (GObject *object,
+ide_frame_header_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
- IdeLayoutStackHeader *self = IDE_LAYOUT_STACK_HEADER (object);
+ IdeFrameHeader *self = IDE_FRAME_HEADER (object);
switch (prop_id)
{
case PROP_BACKGROUND_RGBA:
- _ide_layout_stack_header_set_background_rgba (self, g_value_get_boxed (value));
+ _ide_frame_header_set_background_rgba (self, g_value_get_boxed (value));
break;
case PROP_FOREGROUND_RGBA:
- _ide_layout_stack_header_set_foreground_rgba (self, g_value_get_boxed (value));
+ _ide_frame_header_set_foreground_rgba (self, g_value_get_boxed (value));
break;
case PROP_MODIFIED:
- _ide_layout_stack_header_set_modified (self, g_value_get_boolean (value));
+ _ide_frame_header_set_modified (self, g_value_get_boolean (value));
break;
case PROP_SHOW_CLOSE_BUTTON:
@@ -568,7 +567,7 @@ ide_layout_stack_header_set_property (GObject *object,
break;
case PROP_TITLE:
- _ide_layout_stack_header_set_title (self, g_value_get_string (value));
+ _ide_frame_header_set_title (self, g_value_get_string (value));
break;
default:
@@ -577,26 +576,26 @@ ide_layout_stack_header_set_property (GObject *object,
}
static void
-ide_layout_stack_header_class_init (IdeLayoutStackHeaderClass *klass)
+ide_frame_header_class_init (IdeFrameHeaderClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
- object_class->get_property = ide_layout_stack_header_get_property;
- object_class->set_property = ide_layout_stack_header_set_property;
+ object_class->get_property = ide_frame_header_get_property;
+ object_class->set_property = ide_frame_header_set_property;
- widget_class->destroy = ide_layout_stack_header_destroy;
- widget_class->get_preferred_width = ide_layout_stack_header_get_preferred_width;
+ widget_class->destroy = ide_frame_header_destroy;
+ widget_class->get_preferred_width = ide_frame_header_get_preferred_width;
- container_class->add = ide_layout_stack_header_add;
+ container_class->add = ide_frame_header_add;
/**
- * IdeLayoutStackHeader:background-rgba:
+ * IdeFrameHeader:background-rgba:
*
* The "background-rgba" property can be used to set the background
* color of the header. This should be set to the
- * #IdeLayoutView:primary-color of the active view.
+ * #IdePage:primary-color of the active view.
*
* Set to %NULL to unset the primary-color.
*
@@ -610,10 +609,10 @@ ide_layout_stack_header_class_init (IdeLayoutStackHeaderClass *klass)
(G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
/**
- * IdeLayoutStackHeader:foreground-rgba:
+ * IdeFrameHeader:foreground-rgba:
*
* Sets the foreground color to use when
- * #IdeLayoutStackHeader:background-rgba is used for the background.
+ * #IdeFrameHeader:background-rgba is used for the background.
*
* Since: 3.32
*/
@@ -647,21 +646,21 @@ ide_layout_stack_header_class_init (IdeLayoutStackHeaderClass *klass)
g_object_class_install_properties (object_class, N_PROPS, properties);
- gtk_widget_class_set_css_name (widget_class, "idelayoutstackheader");
- gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/builder/ui/ide-layout-stack-header.ui");
- gtk_widget_class_bind_template_child (widget_class, IdeLayoutStackHeader, close_button);
- gtk_widget_class_bind_template_child (widget_class, IdeLayoutStackHeader, document_button);
- gtk_widget_class_bind_template_child (widget_class, IdeLayoutStackHeader, title_box);
- gtk_widget_class_bind_template_child (widget_class, IdeLayoutStackHeader, title_button);
- gtk_widget_class_bind_template_child (widget_class, IdeLayoutStackHeader, title_label);
- gtk_widget_class_bind_template_child (widget_class, IdeLayoutStackHeader, title_list_box);
- gtk_widget_class_bind_template_child (widget_class, IdeLayoutStackHeader, title_modified);
- gtk_widget_class_bind_template_child (widget_class, IdeLayoutStackHeader, title_popover);
- gtk_widget_class_bind_template_child (widget_class, IdeLayoutStackHeader, title_views_box);
+ gtk_widget_class_set_css_name (widget_class, "ideframeheader");
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/libide-gui/ui/ide-frame-header.ui");
+ gtk_widget_class_bind_template_child (widget_class, IdeFrameHeader, close_button);
+ gtk_widget_class_bind_template_child (widget_class, IdeFrameHeader, document_button);
+ gtk_widget_class_bind_template_child (widget_class, IdeFrameHeader, title_box);
+ gtk_widget_class_bind_template_child (widget_class, IdeFrameHeader, title_button);
+ gtk_widget_class_bind_template_child (widget_class, IdeFrameHeader, title_label);
+ gtk_widget_class_bind_template_child (widget_class, IdeFrameHeader, title_list_box);
+ gtk_widget_class_bind_template_child (widget_class, IdeFrameHeader, title_modified);
+ gtk_widget_class_bind_template_child (widget_class, IdeFrameHeader, title_popover);
+ gtk_widget_class_bind_template_child (widget_class, IdeFrameHeader, title_views_box);
}
static void
-ide_layout_stack_header_init (IdeLayoutStackHeader *self)
+ide_frame_header_init (IdeFrameHeader *self)
{
GtkStyleContext *style_context;
GMenu *frame_section;
@@ -692,7 +691,7 @@ ide_layout_stack_header_init (IdeLayoutStackHeader *self)
self->menu = dzl_joined_menu_new ();
dzl_menu_button_set_model (self->document_button, G_MENU_MODEL (self->menu));
frame_section = dzl_application_get_menu_by_id (DZL_APPLICATION_DEFAULT,
- "ide-layout-stack-frame-menu");
+ "ide-frame-menu");
dzl_joined_menu_append_menu (self->menu, G_MENU_MODEL (frame_section));
/*
@@ -702,7 +701,7 @@ ide_layout_stack_header_init (IdeLayoutStackHeader *self)
g_signal_connect_object (self->title_list_box,
"row-activated",
- G_CALLBACK (ide_layout_stack_header_view_row_activated),
+ G_CALLBACK (ide_frame_header_view_row_activated),
self, 0);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
@@ -711,21 +710,21 @@ ide_layout_stack_header_init (IdeLayoutStackHeader *self)
}
GtkWidget *
-ide_layout_stack_header_new (void)
+ide_frame_header_new (void)
{
- return g_object_new (IDE_TYPE_LAYOUT_STACK_HEADER, NULL);
+ return g_object_new (IDE_TYPE_FRAME_HEADER, NULL);
}
/**
- * ide_layout_stack_header_add_custom_title:
- * @self: a #IdeLayoutStackHeader
+ * ide_frame_header_add_custom_title:
+ * @self: a #IdeFrameHeader
* @widget: a #GtkWidget
* @priority: the sort priority
*
* This will add @widget to the title area with @priority determining the
* sort order of the child.
*
- * All "title" widgets in the #IdeLayoutStackHeader are expanded to the
+ * All "title" widgets in the #IdeFrameHeader are expanded to the
* same size. So if you don't need that, you should just use the normal
* gtk_container_add_with_properties() API to specify your widget with
* a given priority.
@@ -733,11 +732,11 @@ ide_layout_stack_header_new (void)
* Since: 3.32
*/
void
-ide_layout_stack_header_add_custom_title (IdeLayoutStackHeader *self,
+ide_frame_header_add_custom_title (IdeFrameHeader *self,
GtkWidget *widget,
gint priority)
{
- g_return_if_fail (IDE_IS_LAYOUT_STACK_HEADER (self));
+ g_return_if_fail (IDE_IS_FRAME_HEADER (self));
g_return_if_fail (GTK_IS_WIDGET (widget));
gtk_container_add_with_properties (GTK_CONTAINER (self->title_box), widget,
@@ -748,20 +747,20 @@ ide_layout_stack_header_add_custom_title (IdeLayoutStackHeader *self,
}
void
-_ide_layout_stack_header_set_title (IdeLayoutStackHeader *self,
+_ide_frame_header_set_title (IdeFrameHeader *self,
const gchar *title)
{
- g_return_if_fail (IDE_IS_LAYOUT_STACK_HEADER (self));
+ g_return_if_fail (IDE_IS_FRAME_HEADER (self));
gtk_label_set_label (GTK_LABEL (self->title_label), title);
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TITLE]);
}
void
-_ide_layout_stack_header_set_modified (IdeLayoutStackHeader *self,
+_ide_frame_header_set_modified (IdeFrameHeader *self,
gboolean modified)
{
- g_return_if_fail (IDE_IS_LAYOUT_STACK_HEADER (self));
+ g_return_if_fail (IDE_IS_FRAME_HEADER (self));
gtk_widget_set_visible (GTK_WIDGET (self->title_modified), modified);
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MODIFIED]);
diff --git a/src/libide/layout/ide-layout-stack-header.h b/src/libide/gui/ide-frame-header.h
similarity index 60%
rename from src/libide/layout/ide-layout-stack-header.h
rename to src/libide/gui/ide-frame-header.h
index b33050257..26419db07 100644
--- a/src/libide/layout/ide-layout-stack-header.h
+++ b/src/libide/gui/ide-frame-header.h
@@ -1,4 +1,4 @@
-/* ide-layout-stack-header.h
+/* ide-frame-header.h
*
* Copyright 2017-2019 Christian Hergert <chergert redhat com>
*
@@ -20,22 +20,25 @@
#pragma once
-#include <dazzle.h>
+#if !defined (IDE_GUI_INSIDE) && !defined (IDE_GUI_COMPILATION)
+# error "Only <libide-gui.h> can be included directly."
+#endif
-#include "ide-version-macros.h"
+#include <dazzle.h>
+#include <libide-core.h>
G_BEGIN_DECLS
-#define IDE_TYPE_LAYOUT_STACK_HEADER (ide_layout_stack_header_get_type())
+#define IDE_TYPE_FRAME_HEADER (ide_frame_header_get_type())
IDE_AVAILABLE_IN_3_32
-G_DECLARE_FINAL_TYPE (IdeLayoutStackHeader, ide_layout_stack_header, IDE, LAYOUT_STACK_HEADER,
DzlPriorityBox)
+G_DECLARE_FINAL_TYPE (IdeFrameHeader, ide_frame_header, IDE, FRAME_HEADER, DzlPriorityBox)
IDE_AVAILABLE_IN_3_32
-GtkWidget *ide_layout_stack_header_new (void);
+GtkWidget *ide_frame_header_new (void);
IDE_AVAILABLE_IN_3_32
-void ide_layout_stack_header_add_custom_title (IdeLayoutStackHeader *self,
- GtkWidget *widget,
- gint priority);
+void ide_frame_header_add_custom_title (IdeFrameHeader *self,
+ GtkWidget *widget,
+ gint priority);
G_END_DECLS
diff --git a/src/libide/layout/ide-layout-stack-header.ui b/src/libide/gui/ide-frame-header.ui
similarity index 98%
rename from src/libide/layout/ide-layout-stack-header.ui
rename to src/libide/gui/ide-frame-header.ui
index ba8f583ac..a9a8b776d 100644
--- a/src/libide/layout/ide-layout-stack-header.ui
+++ b/src/libide/gui/ide-frame-header.ui
@@ -112,7 +112,7 @@
</object>
</child>
</object>
- <template class="IdeLayoutStackHeader" parent="DzlPriorityBox">
+ <template class="IdeFrameHeader" parent="DzlPriorityBox">
<child>
<object class="DzlPriorityBox" id="title_box">
<property name="hexpand">true</property>
@@ -153,7 +153,7 @@
</child>
<child>
<object class="GtkButton" id="close_button">
- <property name="action-name">layoutgridcolumn.close</property>
+ <property name="action-name">gridcolumn.close</property>
<property name="visible">true</property>
<child>
<object class="GtkImage">
diff --git a/src/libide/layout/ide-layout-stack-shortcuts.c b/src/libide/gui/ide-frame-shortcuts.c
similarity index 78%
rename from src/libide/layout/ide-layout-stack-shortcuts.c
rename to src/libide/gui/ide-frame-shortcuts.c
index da1c3a1ef..eef3655b6 100644
--- a/src/libide/layout/ide-layout-stack-shortcuts.c
+++ b/src/libide/gui/ide-frame-shortcuts.c
@@ -1,4 +1,4 @@
-/* ide-layout-stack-shortcuts.c
+/* ide-frame-shortcuts.c
*
* Copyright 2017-2019 Christian Hergert <chergert redhat com>
*
@@ -18,45 +18,46 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
+
#include "config.h"
#include <glib/gi18n.h>
-#include "layout/ide-layout-stack.h"
-#include "layout/ide-layout-private.h"
+#include "ide-frame.h"
+#include "ide-gui-private.h"
#define I_(s) g_intern_static_string(s)
-static const DzlShortcutEntry stack_shortcuts[] = {
- { "org.gnome.builder.layoutstack.move-right",
+static const DzlShortcutEntry frame_shortcuts[] = {
+ { "org.gnome.builder.frame.move-right",
DZL_SHORTCUT_PHASE_CAPTURE,
NULL,
NC_("shortcut window", "Editor shortcuts"),
NC_("shortcut window", "Files"),
NC_("shortcut window", "Move document to the right") },
- { "org.gnome.builder.layoutstack.move-left",
+ { "org.gnome.builder.frame.move-left",
DZL_SHORTCUT_PHASE_CAPTURE,
NULL,
NC_("shortcut window", "Editor shortcuts"),
NC_("shortcut window", "Files"),
NC_("shortcut window", "Move document to the left") },
- { "org.gnome.builder.layoutstack.previous-document",
+ { "org.gnome.builder.frame.previous-document",
DZL_SHORTCUT_PHASE_CAPTURE,
NULL,
NC_("shortcut window", "Editor shortcuts"),
NC_("shortcut window", "Files"),
NC_("shortcut window", "Switch to the previous document") },
- { "org.gnome.builder.layoutstack.next-document",
+ { "org.gnome.builder.frame.next-document",
DZL_SHORTCUT_PHASE_CAPTURE,
NULL,
NC_("shortcut window", "Editor shortcuts"),
NC_("shortcut window", "Files"),
NC_("shortcut window", "Switch to the next document") },
- { "org.gnome.builder.layoutstack.close-view",
+ { "org.gnome.builder.frame.close-page",
DZL_SHORTCUT_PHASE_BUBBLE,
NULL,
NC_("shortcut window", "Editor shortcuts"),
@@ -65,48 +66,48 @@ static const DzlShortcutEntry stack_shortcuts[] = {
};
void
-_ide_layout_stack_init_shortcuts (IdeLayoutStack *self)
+_ide_frame_init_shortcuts (IdeFrame *self)
{
DzlShortcutController *controller;
- g_return_if_fail (IDE_IS_LAYOUT_STACK (self));
+ g_return_if_fail (IDE_IS_FRAME (self));
dzl_shortcut_manager_add_shortcut_entries (NULL,
- stack_shortcuts,
- G_N_ELEMENTS (stack_shortcuts),
+ frame_shortcuts,
+ G_N_ELEMENTS (frame_shortcuts),
GETTEXT_PACKAGE);
controller = dzl_shortcut_controller_find (GTK_WIDGET (self));
dzl_shortcut_controller_add_command_action (controller,
- I_("org.gnome.builder.layoutstack.move-right"),
+ I_("org.gnome.builder.frame.move-right"),
I_("<Primary><Alt>Page_Down"),
DZL_SHORTCUT_PHASE_BUBBLE,
- I_("layoutstack.move-right"));
+ I_("frame.move-right"));
dzl_shortcut_controller_add_command_action (controller,
- I_("org.gnome.builder.layoutstack.move-left"),
+ I_("org.gnome.builder.frame.move-left"),
I_("<Primary><Alt>Page_Up"),
DZL_SHORTCUT_PHASE_BUBBLE,
- I_("layoutstack.move-left"));
+ I_("frame.move-left"));
dzl_shortcut_controller_add_command_signal (controller,
- I_("org.gnome.builder.layoutstack.next-document"),
+ I_("org.gnome.builder.frame.next-document"),
I_("<Primary><Shift>Page_Down"),
DZL_SHORTCUT_PHASE_BUBBLE,
I_("change-current-page"),
1, G_TYPE_INT, 1);
dzl_shortcut_controller_add_command_signal (controller,
- I_("org.gnome.builder.layoutstack.previous-document"),
+ I_("org.gnome.builder.frame.previous-document"),
I_("<Primary><Shift>Page_Up"),
DZL_SHORTCUT_PHASE_BUBBLE,
I_("change-current-page"),
1, G_TYPE_INT, -1);
dzl_shortcut_controller_add_command_action (controller,
- I_("org.gnome.builder.layoutstack.close-view"),
+ I_("org.gnome.builder.frame.close-page"),
I_("<Primary>w"),
DZL_SHORTCUT_PHASE_BUBBLE,
- I_("layoutstack.close-view"));
+ I_("frame.close-page"));
}
diff --git a/src/libide/layout/ide-layout-stack-wrapper.c b/src/libide/gui/ide-frame-wrapper.c
similarity index 68%
rename from src/libide/layout/ide-layout-stack-wrapper.c
rename to src/libide/gui/ide-frame-wrapper.c
index 45e42ec04..aacd241f5 100644
--- a/src/libide/layout/ide-layout-stack-wrapper.c
+++ b/src/libide/gui/ide-frame-wrapper.c
@@ -1,4 +1,4 @@
-/* ide-layout-stack-wrapper.c
+/* ide-frame-wrapper.c
*
* Copyright 2018-2019 Christian Hergert <chergert redhat com>
*
@@ -18,11 +18,11 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
-#define G_LOG_DOMAIN "ide-layout-stack-wrapper"
+#define G_LOG_DOMAIN "ide-frame-wrapper"
#include "config.h"
-#include "ide-layout-stack-wrapper.h"
+#include "ide-frame-wrapper.h"
/*
* This is just a GtkStack wrapper that allows us to override
@@ -30,21 +30,21 @@
* focused child first.
*/
-struct _IdeLayoutStackWrapper
+struct _IdeFrameWrapper
{
GtkStack parent_instance;
GQueue history;
};
-G_DEFINE_TYPE (IdeLayoutStackWrapper, ide_layout_stack_wrapper, GTK_TYPE_STACK)
+G_DEFINE_TYPE (IdeFrameWrapper, ide_frame_wrapper, GTK_TYPE_STACK)
static void
-ide_layout_stack_wrapper_add (GtkContainer *container,
+ide_frame_wrapper_add (GtkContainer *container,
GtkWidget *widget)
{
- IdeLayoutStackWrapper *self = (IdeLayoutStackWrapper *)container;
+ IdeFrameWrapper *self = (IdeFrameWrapper *)container;
- g_assert (IDE_IS_LAYOUT_STACK_WRAPPER (container));
+ g_assert (IDE_IS_FRAME_WRAPPER (container));
g_assert (GTK_IS_WIDGET (widget));
if (gtk_widget_get_visible (widget))
@@ -52,16 +52,16 @@ ide_layout_stack_wrapper_add (GtkContainer *container,
else
g_queue_push_tail (&self->history, widget);
- GTK_CONTAINER_CLASS (ide_layout_stack_wrapper_parent_class)->add (container, widget);
+ GTK_CONTAINER_CLASS (ide_frame_wrapper_parent_class)->add (container, widget);
}
static void
-ide_layout_stack_wrapper_remove (GtkContainer *container,
+ide_frame_wrapper_remove (GtkContainer *container,
GtkWidget *widget)
{
- IdeLayoutStackWrapper *self = (IdeLayoutStackWrapper *)container;
+ IdeFrameWrapper *self = (IdeFrameWrapper *)container;
- g_assert (IDE_IS_LAYOUT_STACK_WRAPPER (container));
+ g_assert (IDE_IS_FRAME_WRAPPER (container));
g_assert (GTK_IS_WIDGET (widget));
/* Remove the widget from our history chain, and then see if we need to
@@ -79,16 +79,16 @@ ide_layout_stack_wrapper_remove (GtkContainer *container,
gtk_stack_set_visible_child (GTK_STACK (self), new_fg);
}
- GTK_CONTAINER_CLASS (ide_layout_stack_wrapper_parent_class)->remove (container, widget);
+ GTK_CONTAINER_CLASS (ide_frame_wrapper_parent_class)->remove (container, widget);
}
static void
-ide_layout_stack_wrapper_notify_visible_child (IdeLayoutStackWrapper *self,
+ide_frame_wrapper_notify_visible_child (IdeFrameWrapper *self,
GParamSpec *pspec)
{
GtkWidget *visible_child;
- g_assert (IDE_IS_LAYOUT_STACK_WRAPPER (self));
+ g_assert (IDE_IS_FRAME_WRAPPER (self));
g_assert (pspec != NULL);
if ((visible_child = gtk_stack_get_visible_child (GTK_STACK (self))))
@@ -106,19 +106,19 @@ ide_layout_stack_wrapper_notify_visible_child (IdeLayoutStackWrapper *self,
}
static void
-ide_layout_stack_wrapper_class_init (IdeLayoutStackWrapperClass *klass)
+ide_frame_wrapper_class_init (IdeFrameWrapperClass *klass)
{
GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
- container_class->add = ide_layout_stack_wrapper_add;
- container_class->remove = ide_layout_stack_wrapper_remove;
+ container_class->add = ide_frame_wrapper_add;
+ container_class->remove = ide_frame_wrapper_remove;
}
static void
-ide_layout_stack_wrapper_init (IdeLayoutStackWrapper *self)
+ide_frame_wrapper_init (IdeFrameWrapper *self)
{
g_signal_connect (self,
"notify::visible-child",
- G_CALLBACK (ide_layout_stack_wrapper_notify_visible_child),
+ G_CALLBACK (ide_frame_wrapper_notify_visible_child),
NULL);
}
diff --git a/src/libide/layout/ide-layout-stack-wrapper.h b/src/libide/gui/ide-frame-wrapper.h
similarity index 79%
rename from src/libide/layout/ide-layout-stack-wrapper.h
rename to src/libide/gui/ide-frame-wrapper.h
index 07cb26746..093aaa780 100644
--- a/src/libide/layout/ide-layout-stack-wrapper.h
+++ b/src/libide/gui/ide-frame-wrapper.h
@@ -1,4 +1,4 @@
-/* ide-layout-stack-wrapper.h
+/* ide-frame-wrapper.h
*
* Copyright 2018-2019 Christian Hergert <chergert redhat com>
*
@@ -24,8 +24,8 @@
G_BEGIN_DECLS
-#define IDE_TYPE_LAYOUT_STACK_WRAPPER (ide_layout_stack_wrapper_get_type())
+#define IDE_TYPE_FRAME_WRAPPER (ide_frame_wrapper_get_type())
-G_DECLARE_FINAL_TYPE (IdeLayoutStackWrapper, ide_layout_stack_wrapper, IDE, LAYOUT_STACK_WRAPPER, GtkStack)
+G_DECLARE_FINAL_TYPE (IdeFrameWrapper, ide_frame_wrapper, IDE, FRAME_WRAPPER, GtkStack)
G_END_DECLS
diff --git a/src/libide/layout/ide-layout-stack.c b/src/libide/gui/ide-frame.c
similarity index 54%
rename from src/libide/layout/ide-layout-stack.c
rename to src/libide/gui/ide-frame.c
index 857f4c785..15f477930 100644
--- a/src/libide/layout/ide-layout-stack.c
+++ b/src/libide/gui/ide-frame.c
@@ -1,4 +1,4 @@
-/* ide-layout-stack.c
+/* ide-frame.c
*
* Copyright 2017-2019 Christian Hergert <chergert redhat com>
*
@@ -18,41 +18,41 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
-#define G_LOG_DOMAIN "ide-layout-stack"
+
+
+#define G_LOG_DOMAIN "ide-frame"
#include "config.h"
#include <dazzle.h>
#include <glib/gi18n.h>
+#include <libide-core.h>
+#include <libide-threading.h>
#include <libpeas/peas.h>
-#include "ide-debug.h"
-
-#include "layout/ide-layout-stack.h"
-#include "layout/ide-layout-stack-addin.h"
-#include "layout/ide-layout-stack-header.h"
-#include "layout/ide-layout-stack-wrapper.h"
-#include "layout/ide-layout-private.h"
-#include "layout/ide-shortcut-label.h"
-#include "threading/ide-task.h"
+#include "ide-frame.h"
+#include "ide-frame-addin.h"
+#include "ide-frame-header.h"
+#include "ide-frame-wrapper.h"
+#include "ide-gui-private.h"
#define TRANSITION_DURATION 300
#define DISTANCE_THRESHOLD(alloc) (MIN(250, (gint)((alloc)->width * .333)))
/**
- * SECTION:ide-layout-stack
- * @title: IdeLayoutStack
- * @short_description: A stack of #IdeLayoutView
+ * SECTION:ide-frame
+ * @title: IdeFrame
+ * @short_description: A stack of #IdePage
*
- * This widget is used to represent a stack of #IdeLayoutView widgets. it
- * includes an #IdeLayoutStackHeader at the top, and then a stack of views
+ * This widget is used to represent a stack of #IdePage widgets. it
+ * includes an #IdeFrameHeader at the top, and then a stack of pages
* below.
*
- * If there are no #IdeLayoutView visibile, then an empty state widget is
+ * If there are no #IdePage visibile, then an empty state widget is
* displayed with some common information for the user.
*
- * To simplify integration with other systems, #IdeLayoutStack implements
- * the #GListModel interface for each of the #IdeLayoutView.
+ * To simplify integration with other systems, #IdeFrame implements
+ * the #GListModel interface for each of the #IdePage.
*
* Since: 3.32
*/
@@ -61,7 +61,7 @@ typedef struct
{
DzlBindingGroup *bindings;
DzlSignalGroup *signals;
- GPtrArray *views;
+ GPtrArray *pages;
GPtrArray *in_transition;
PeasExtensionSet *addins;
@@ -76,22 +76,22 @@ typedef struct
GtkGesture *dummy;
GtkGesture *pan;
DzlBoxTheatric *pan_theatric;
- IdeLayoutView *pan_view;
+ IdePage *pan_page;
/* Template references */
DzlBox *empty_state;
DzlEmptyState *failed_state;
- IdeLayoutStackHeader *header;
+ IdeFrameHeader *header;
GtkStack *stack;
GtkStack *top_stack;
GtkEventBox *event_box;
-} IdeLayoutStackPrivate;
+} IdeFramePrivate;
typedef struct
{
- IdeLayoutStack *source;
- IdeLayoutStack *dest;
- IdeLayoutView *view;
+ IdeFrame *source;
+ IdeFrame *dest;
+ IdePage *page;
DzlBoxTheatric *theatric;
} AnimationState;
@@ -110,8 +110,8 @@ enum {
static void list_model_iface_init (GListModelInterface *iface);
static void animation_state_complete (gpointer data);
-G_DEFINE_TYPE_WITH_CODE (IdeLayoutStack, ide_layout_stack, GTK_TYPE_BOX,
- G_ADD_PRIVATE (IdeLayoutStack)
+G_DEFINE_TYPE_WITH_CODE (IdeFrame, ide_frame, GTK_TYPE_BOX,
+ G_ADD_PRIVATE (IdeFrame)
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init))
static GParamSpec *properties [N_PROPS];
@@ -125,14 +125,14 @@ is_uninitialized (GtkAllocation *alloc)
}
static void
-ide_layout_stack_set_cursor (IdeLayoutStack *self,
- const gchar *name)
+ide_frame_set_cursor (IdeFrame *self,
+ const gchar *name)
{
GdkWindow *window;
GdkDisplay *display;
GdkCursor *cursor;
- g_assert (IDE_IS_LAYOUT_STACK (self));
+ g_assert (IDE_IS_FRAME (self));
g_assert (name != NULL);
window = gtk_widget_get_window (GTK_WIDGET (self));
@@ -145,70 +145,70 @@ ide_layout_stack_set_cursor (IdeLayoutStack *self,
}
static void
-ide_layout_stack_view_failed (IdeLayoutStack *self,
- GParamSpec *pspec,
- IdeLayoutView *view)
+ide_frame_page_failed (IdeFrame *self,
+ GParamSpec *pspec,
+ IdePage *page)
{
- IdeLayoutStackPrivate *priv = ide_layout_stack_get_instance_private (self);
+ IdeFramePrivate *priv = ide_frame_get_instance_private (self);
- g_assert (IDE_IS_LAYOUT_STACK (self));
- g_assert (IDE_IS_LAYOUT_VIEW (view));
+ g_assert (IDE_IS_FRAME (self));
+ g_assert (IDE_IS_PAGE (page));
- if (ide_layout_view_get_failed (view))
+ if (ide_page_get_failed (page))
gtk_stack_set_visible_child (priv->top_stack, GTK_WIDGET (priv->failed_state));
else
gtk_stack_set_visible_child (priv->top_stack, GTK_WIDGET (priv->stack));
}
static void
-ide_layout_stack_bindings_notify_source (IdeLayoutStack *self,
- GParamSpec *pspec,
- DzlBindingGroup *bindings)
+ide_frame_bindings_notify_source (IdeFrame *self,
+ GParamSpec *pspec,
+ DzlBindingGroup *bindings)
{
- IdeLayoutStackPrivate *priv = ide_layout_stack_get_instance_private (self);
+ IdeFramePrivate *priv = ide_frame_get_instance_private (self);
GObject *source;
g_assert (DZL_IS_BINDING_GROUP (bindings));
g_assert (pspec != NULL);
- g_assert (IDE_IS_LAYOUT_STACK (self));
+ g_assert (IDE_IS_FRAME (self));
source = dzl_binding_group_get_source (bindings);
if (source == NULL)
{
- _ide_layout_stack_header_set_title (priv->header, _("No Open Pages"));
- _ide_layout_stack_header_set_modified (priv->header, FALSE);
- _ide_layout_stack_header_set_background_rgba (priv->header, NULL);
- _ide_layout_stack_header_set_foreground_rgba (priv->header, NULL);
+ _ide_frame_header_set_title (priv->header, _("No Open Pages"));
+ _ide_frame_header_set_modified (priv->header, FALSE);
+ _ide_frame_header_set_background_rgba (priv->header, NULL);
+ _ide_frame_header_set_foreground_rgba (priv->header, NULL);
}
}
static void
-ide_layout_stack_notify_addin_of_view (PeasExtensionSet *set,
- PeasPluginInfo *plugin_info,
- PeasExtension *exten,
- gpointer user_data)
+ide_frame_notify_addin_of_page (PeasExtensionSet *set,
+ PeasPluginInfo *plugin_info,
+ PeasExtension *exten,
+ gpointer user_data)
{
- IdeLayoutStackAddin *addin = (IdeLayoutStackAddin *)exten;
- IdeLayoutView *view = user_data;
+ IdeFrameAddin *addin = (IdeFrameAddin *)exten;
+ IdePage *page = user_data;
g_assert (PEAS_IS_EXTENSION_SET (set));
g_assert (plugin_info != NULL);
- g_assert (IDE_IS_LAYOUT_STACK_ADDIN (addin));
- g_assert (!view || IDE_IS_LAYOUT_VIEW (view));
+ g_assert (IDE_IS_FRAME_ADDIN (addin));
+ g_assert (!page || IDE_IS_PAGE (page));
- ide_layout_stack_addin_set_view (addin, view);
+ ide_frame_addin_set_page (addin, page);
}
static void
-ide_layout_stack_notify_visible_child (IdeLayoutStack *self,
- GParamSpec *pspec,
- GtkStack *stack)
+ide_frame_notify_visible_child (IdeFrame *self,
+ GParamSpec *pspec,
+ GtkStack *stack)
{
- IdeLayoutStackPrivate *priv = ide_layout_stack_get_instance_private (self);
+ IdeFramePrivate *priv = ide_frame_get_instance_private (self);
GtkWidget *visible_child;
- g_assert (IDE_IS_LAYOUT_STACK (self));
+ g_assert (IDE_IS_FRAME (self));
g_assert (GTK_IS_STACK (stack));
if (gtk_widget_in_destruction (GTK_WIDGET (self)))
@@ -225,15 +225,15 @@ ide_layout_stack_notify_visible_child (IdeLayoutStack *self,
* from the header bar without any weirdness by the View.
*/
dzl_gtk_widget_mux_action_groups (GTK_WIDGET (self), visible_child,
- "IDE_LAYOUT_STACK_MUXED_ACTION");
+ "IDE_FRAME_MUXED_ACTION");
/* Update our bindings targets */
dzl_binding_group_set_source (priv->bindings, visible_child);
dzl_signal_group_set_target (priv->signals, visible_child);
- /* Show either the empty state, failed state, or actual view */
+ /* Show either the empty state, failed state, or actual page */
if (visible_child != NULL &&
- ide_layout_view_get_failed (IDE_LAYOUT_VIEW (visible_child)))
+ ide_page_get_failed (IDE_PAGE (visible_child)))
gtk_stack_set_visible_child (priv->top_stack, GTK_WIDGET (priv->failed_state));
else if (visible_child != NULL)
gtk_stack_set_visible_child (priv->top_stack, GTK_WIDGET (priv->stack));
@@ -241,14 +241,15 @@ ide_layout_stack_notify_visible_child (IdeLayoutStack *self,
gtk_stack_set_visible_child (priv->top_stack, GTK_WIDGET (priv->empty_state));
/* Allow the header to update settings */
- _ide_layout_stack_header_update (priv->header, IDE_LAYOUT_VIEW (visible_child));
+ _ide_frame_header_update (priv->header, IDE_PAGE (visible_child));
/* Ensure action state is up to date */
- _ide_layout_stack_update_actions (self);
+ _ide_frame_update_actions (self);
- peas_extension_set_foreach (priv->addins,
- ide_layout_stack_notify_addin_of_view,
- visible_child);
+ if (priv->addins != NULL)
+ peas_extension_set_foreach (priv->addins,
+ ide_frame_notify_addin_of_page,
+ visible_child);
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_VISIBLE_CHILD]);
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HAS_VIEW]);
@@ -262,15 +263,15 @@ collect_widgets (GtkWidget *widget,
}
static void
-ide_layout_stack_change_current_page (IdeLayoutStack *self,
- gint direction)
+ide_frame_change_current_page (IdeFrame *self,
+ gint direction)
{
- IdeLayoutStackPrivate *priv = ide_layout_stack_get_instance_private (self);
+ IdeFramePrivate *priv = ide_frame_get_instance_private (self);
g_autoptr(GPtrArray) ar = NULL;
GtkWidget *visible_child;
gint position = 0;
- g_assert (IDE_IS_LAYOUT_STACK (self));
+ g_assert (IDE_IS_FRAME (self));
visible_child = gtk_stack_get_visible_child (priv->stack);
@@ -291,85 +292,85 @@ ide_layout_stack_change_current_page (IdeLayoutStack *self,
}
static void
-ide_layout_stack_add (GtkContainer *container,
- GtkWidget *widget)
+ide_frame_add (GtkContainer *container,
+ GtkWidget *widget)
{
- IdeLayoutStack *self = (IdeLayoutStack *)container;
- IdeLayoutStackPrivate *priv = ide_layout_stack_get_instance_private (self);
+ IdeFrame *self = (IdeFrame *)container;
+ IdeFramePrivate *priv = ide_frame_get_instance_private (self);
- g_return_if_fail (IDE_IS_LAYOUT_STACK (self));
+ g_return_if_fail (IDE_IS_FRAME (self));
g_return_if_fail (GTK_IS_WIDGET (widget));
- if (IDE_IS_LAYOUT_VIEW (widget))
+ if (IDE_IS_PAGE (widget))
gtk_container_add (GTK_CONTAINER (priv->stack), widget);
else
- GTK_CONTAINER_CLASS (ide_layout_stack_parent_class)->add (container, widget);
+ GTK_CONTAINER_CLASS (ide_frame_parent_class)->add (container, widget);
gtk_widget_queue_resize (GTK_WIDGET (self));
}
static void
-ide_layout_stack_view_added (IdeLayoutStack *self,
- IdeLayoutView *view)
+ide_frame_page_added (IdeFrame *self,
+ IdePage *page)
{
- IdeLayoutStackPrivate *priv = ide_layout_stack_get_instance_private (self);
+ IdeFramePrivate *priv = ide_frame_get_instance_private (self);
guint position;
- g_assert (IDE_IS_LAYOUT_STACK (self));
- g_assert (IDE_IS_LAYOUT_VIEW (view));
+ g_assert (IDE_IS_FRAME (self));
+ g_assert (IDE_IS_PAGE (page));
/*
* Make sure that the header has dismissed all of the popovers immediately.
* We don't want them lingering while we do other UI work which might want to
* grab focus, etc.
*/
- _ide_layout_stack_header_popdown (priv->header);
+ _ide_frame_header_popdown (priv->header);
- /* Notify GListModel consumers of the new view and it's position within
- * our stack of view widgets.
+ /* Notify GListModel consumers of the new page and it's position within
+ * our stack of page widgets.
*/
- position = priv->views->len;
- g_ptr_array_add (priv->views, view);
+ position = priv->pages->len;
+ g_ptr_array_add (priv->pages, page);
g_list_model_items_changed (G_LIST_MODEL (self), position, 0, 1);
/*
- * Now ensure that the view is displayed and focus the widget so the
+ * Now ensure that the page is displayed and focus the widget so the
* user can immediately start typing.
*/
- ide_layout_stack_set_visible_child (self, view);
- gtk_widget_grab_focus (GTK_WIDGET (view));
+ ide_frame_set_visible_child (self, page);
+ gtk_widget_grab_focus (GTK_WIDGET (page));
}
static void
-ide_layout_stack_view_removed (IdeLayoutStack *self,
- IdeLayoutView *view)
+ide_frame_page_removed (IdeFrame *self,
+ IdePage *page)
{
- IdeLayoutStackPrivate *priv = ide_layout_stack_get_instance_private (self);
+ IdeFramePrivate *priv = ide_frame_get_instance_private (self);
- g_assert (IDE_IS_LAYOUT_STACK (self));
- g_assert (IDE_IS_LAYOUT_VIEW (view));
+ g_assert (IDE_IS_FRAME (self));
+ g_assert (IDE_IS_PAGE (page));
- if (priv->views != NULL)
+ if (priv->pages != NULL)
{
guint position = 0;
- /* If this is the last view, hide the popdown now. We use our hide
+ /* If this is the last page, hide the popdown now. We use our hide
* variant instead of popdown so that we don't have jittery animations.
*/
- if (priv->views->len == 1)
- _ide_layout_stack_header_hide (priv->header);
+ if (priv->pages->len == 1)
+ _ide_frame_header_hide (priv->header);
/*
- * Only remove the view if it is not in transition. We hold onto the
- * view during the transition so that we keep the list stable.
+ * Only remove the page if it is not in transition. We hold onto the
+ * page during the transition so that we keep the list stable.
*/
- if (!g_ptr_array_find_with_equal_func (priv->in_transition, view, NULL, &position))
+ if (!g_ptr_array_find_with_equal_func (priv->in_transition, page, NULL, &position))
{
- for (guint i = 0; i < priv->views->len; i++)
+ for (guint i = 0; i < priv->pages->len; i++)
{
- if ((gpointer)view == g_ptr_array_index (priv->views, i))
+ if ((gpointer)page == g_ptr_array_index (priv->pages, i))
{
- g_ptr_array_remove_index (priv->views, i);
+ g_ptr_array_remove_index (priv->pages, i);
g_list_model_items_changed (G_LIST_MODEL (self), i, 1, 0);
}
}
@@ -378,83 +379,83 @@ ide_layout_stack_view_removed (IdeLayoutStack *self,
}
static void
-ide_layout_stack_real_agree_to_close_async (IdeLayoutStack *self,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+ide_frame_real_agree_to_close_async (IdeFrame *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
g_autoptr(IdeTask) task = NULL;
- g_assert (IDE_IS_LAYOUT_STACK (self));
+ g_assert (IDE_IS_FRAME (self));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
task = ide_task_new (self, cancellable, callback, user_data);
- ide_task_set_source_tag (task, ide_layout_stack_real_agree_to_close_async);
+ ide_task_set_source_tag (task, ide_frame_real_agree_to_close_async);
ide_task_set_priority (task, G_PRIORITY_LOW);
ide_task_return_boolean (task, TRUE);
}
static gboolean
-ide_layout_stack_real_agree_to_close_finish (IdeLayoutStack *self,
- GAsyncResult *result,
- GError **error)
+ide_frame_real_agree_to_close_finish (IdeFrame *self,
+ GAsyncResult *result,
+ GError **error)
{
- g_assert (IDE_IS_LAYOUT_STACK (self));
+ g_assert (IDE_IS_FRAME (self));
g_assert (IDE_IS_TASK (result));
return ide_task_propagate_boolean (IDE_TASK (result), error);
}
static void
-ide_layout_stack_addin_added (PeasExtensionSet *set,
- PeasPluginInfo *plugin_info,
- PeasExtension *exten,
- gpointer user_data)
+ide_frame_addin_added (PeasExtensionSet *set,
+ PeasPluginInfo *plugin_info,
+ PeasExtension *exten,
+ gpointer user_data)
{
- IdeLayoutStackAddin *addin = (IdeLayoutStackAddin *)exten;
- IdeLayoutStack *self = user_data;
- IdeLayoutView *visible_child;
+ IdeFrameAddin *addin = (IdeFrameAddin *)exten;
+ IdeFrame *self = user_data;
+ IdePage *visible_child;
- g_assert (IDE_IS_LAYOUT_STACK (self));
+ g_assert (IDE_IS_FRAME (self));
g_assert (PEAS_IS_EXTENSION_SET (set));
g_assert (plugin_info != NULL);
- g_assert (IDE_IS_LAYOUT_STACK_ADDIN (addin));
+ g_assert (IDE_IS_FRAME_ADDIN (addin));
- ide_layout_stack_addin_load (addin, self);
+ ide_frame_addin_load (addin, self);
- visible_child = ide_layout_stack_get_visible_child (self);
+ visible_child = ide_frame_get_visible_child (self);
if (visible_child != NULL)
- ide_layout_stack_addin_set_view (addin, visible_child);
+ ide_frame_addin_set_page (addin, visible_child);
}
static void
-ide_layout_stack_addin_removed (PeasExtensionSet *set,
- PeasPluginInfo *plugin_info,
- PeasExtension *exten,
- gpointer user_data)
+ide_frame_addin_removed (PeasExtensionSet *set,
+ PeasPluginInfo *plugin_info,
+ PeasExtension *exten,
+ gpointer user_data)
{
- IdeLayoutStackAddin *addin = (IdeLayoutStackAddin *)exten;
- IdeLayoutStack *self = user_data;
+ IdeFrameAddin *addin = (IdeFrameAddin *)exten;
+ IdeFrame *self = user_data;
- g_assert (IDE_IS_LAYOUT_STACK (self));
+ g_assert (IDE_IS_FRAME (self));
g_assert (PEAS_IS_EXTENSION_SET (set));
g_assert (plugin_info != NULL);
- g_assert (IDE_IS_LAYOUT_STACK_ADDIN (addin));
+ g_assert (IDE_IS_FRAME_ADDIN (addin));
- ide_layout_stack_addin_set_view (addin, NULL);
- ide_layout_stack_addin_unload (addin, self);
+ ide_frame_addin_set_page (addin, NULL);
+ ide_frame_addin_unload (addin, self);
}
static gboolean
-ide_layout_stack_pan_begin (IdeLayoutStack *self,
- GdkEventSequence *sequence,
- GtkGesturePan *gesture)
+ide_frame_pan_begin (IdeFrame *self,
+ GdkEventSequence *sequence,
+ GtkGesturePan *gesture)
{
- IdeLayoutStackPrivate *priv = ide_layout_stack_get_instance_private (self);
+ IdeFramePrivate *priv = ide_frame_get_instance_private (self);
GtkAllocation alloc;
cairo_surface_t *surface = NULL;
- IdeLayoutView *view;
+ IdePage *page;
GdkWindow *window;
GtkWidget *grid;
cairo_t *cr;
@@ -463,23 +464,23 @@ ide_layout_stack_pan_begin (IdeLayoutStack *self,
IDE_ENTRY;
- g_assert (IDE_IS_LAYOUT_STACK (self));
+ g_assert (IDE_IS_FRAME (self));
g_assert (GTK_IS_GESTURE_PAN (gesture));
g_assert (priv->pan_theatric == NULL);
- view = ide_layout_stack_get_visible_child (self);
- if (view != NULL)
- gtk_widget_get_allocation (GTK_WIDGET (view), &alloc);
+ page = ide_frame_get_visible_child (self);
+ if (page != NULL)
+ gtk_widget_get_allocation (GTK_WIDGET (page), &alloc);
g_object_get (gtk_settings_get_default (),
"gtk-enable-animations", &enable_animations,
NULL);
if (sequence != NULL ||
- view == NULL ||
+ page == NULL ||
!enable_animations ||
is_uninitialized (&alloc) ||
- NULL == (window = gtk_widget_get_window (GTK_WIDGET (view))) ||
+ NULL == (window = gtk_widget_get_window (GTK_WIDGET (page))) ||
NULL == (surface = gdk_window_create_similar_surface (window,
CAIRO_CONTENT_COLOR,
alloc.width,
@@ -493,14 +494,14 @@ ide_layout_stack_pan_begin (IdeLayoutStack *self,
gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (gesture), &x, &y);
cr = cairo_create (surface);
- gtk_widget_draw (GTK_WIDGET (view), cr);
+ gtk_widget_draw (GTK_WIDGET (page), cr);
cairo_destroy (cr);
- grid = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_LAYOUT_GRID);
+ grid = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_GRID);
gtk_widget_translate_coordinates (GTK_WIDGET (priv->top_stack), grid, 0, 0,
&alloc.x, &alloc.y);
- priv->pan_view = g_object_ref (view);
+ priv->pan_page = g_object_ref (page);
priv->pan_theatric = g_object_new (DZL_TYPE_BOX_THEATRIC,
"surface", surface,
"target", grid,
@@ -512,34 +513,34 @@ ide_layout_stack_pan_begin (IdeLayoutStack *self,
g_clear_pointer (&surface, cairo_surface_destroy);
- /* Hide the view while we begin the possible transition to another
+ /* Hide the page while we begin the possible transition to another
* layout stack.
*/
- gtk_widget_hide (GTK_WIDGET (priv->pan_view));
+ gtk_widget_hide (GTK_WIDGET (priv->pan_page));
/*
- * Hide the mouse cursor until ide_layout_stack_pan_end() is called.
+ * Hide the mouse cursor until ide_frame_pan_end() is called.
* It can be distracting otherwise (and we want to warp it to the new
* grid column too).
*/
- ide_layout_stack_set_cursor (self, "none");
+ ide_frame_set_cursor (self, "none");
IDE_RETURN (TRUE);
}
static void
-ide_layout_stack_pan_update (IdeLayoutStack *self,
- GdkEventSequence *sequence,
- GtkGestureSwipe *gesture)
+ide_frame_pan_update (IdeFrame *self,
+ GdkEventSequence *sequence,
+ GtkGestureSwipe *gesture)
{
- IdeLayoutStackPrivate *priv = ide_layout_stack_get_instance_private (self);
+ IdeFramePrivate *priv = ide_frame_get_instance_private (self);
GtkAllocation alloc;
GtkWidget *grid;
gdouble x, y;
IDE_ENTRY;
- g_assert (IDE_IS_LAYOUT_STACK (self));
+ g_assert (IDE_IS_FRAME (self));
g_assert (GTK_IS_GESTURE_PAN (gesture));
g_assert (!priv->pan_theatric || DZL_IS_BOX_THEATRIC (priv->pan_theatric));
@@ -553,7 +554,7 @@ ide_layout_stack_pan_update (IdeLayoutStack *self,
gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (gesture), &x, &y);
gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
- grid = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_LAYOUT_GRID);
+ grid = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_GRID);
gtk_widget_translate_coordinates (GTK_WIDGET (priv->top_stack), grid, 0, 0,
&alloc.x, &alloc.y);
@@ -565,13 +566,13 @@ ide_layout_stack_pan_update (IdeLayoutStack *self,
}
static void
-ide_layout_stack_pan_end (IdeLayoutStack *self,
- GdkEventSequence *sequence,
- GtkGesturePan *gesture)
+ide_frame_pan_end (IdeFrame *self,
+ GdkEventSequence *sequence,
+ GtkGesturePan *gesture)
{
- IdeLayoutStackPrivate *priv = ide_layout_stack_get_instance_private (self);
- IdeLayoutStackPrivate *dest_priv;
- IdeLayoutStack *dest;
+ IdeFramePrivate *priv = ide_frame_get_instance_private (self);
+ IdeFramePrivate *dest_priv;
+ IdeFrame *dest;
GtkAllocation alloc;
GtkWidget *grid;
GtkWidget *column;
@@ -581,10 +582,10 @@ ide_layout_stack_pan_end (IdeLayoutStack *self,
IDE_ENTRY;
- g_assert (IDE_IS_LAYOUT_STACK (self));
+ g_assert (IDE_IS_FRAME (self));
g_assert (GTK_IS_GESTURE_PAN (gesture));
- if (priv->pan_theatric == NULL || priv->pan_view == NULL)
+ if (priv->pan_theatric == NULL || priv->pan_page == NULL)
IDE_GOTO (cleanup);
gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
@@ -598,22 +599,22 @@ ide_layout_stack_pan_end (IdeLayoutStack *self,
else
direction = 0;
- grid = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_LAYOUT_GRID);
+ grid = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_GRID);
g_assert (grid != NULL);
- g_assert (IDE_IS_LAYOUT_GRID (grid));
+ g_assert (IDE_IS_GRID (grid));
- column = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_LAYOUT_GRID_COLUMN);
+ column = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_GRID_COLUMN);
g_assert (column != NULL);
- g_assert (IDE_IS_LAYOUT_GRID_COLUMN (column));
+ g_assert (IDE_IS_GRID_COLUMN (column));
gtk_container_child_get (GTK_CONTAINER (grid), GTK_WIDGET (column),
"index", &index,
NULL);
- dest = _ide_layout_grid_get_nth_stack (IDE_LAYOUT_GRID (grid), index + direction);
- dest_priv = ide_layout_stack_get_instance_private (dest);
+ dest = _ide_grid_get_nth_stack (IDE_GRID (grid), index + direction);
+ dest_priv = ide_frame_get_instance_private (dest);
g_assert (dest != NULL);
- g_assert (IDE_IS_LAYOUT_STACK (dest));
+ g_assert (IDE_IS_FRAME (dest));
gtk_widget_get_allocation (GTK_WIDGET (dest), &alloc);
@@ -624,7 +625,7 @@ ide_layout_stack_pan_end (IdeLayoutStack *self,
state = g_slice_new0 (AnimationState);
state->source = g_object_ref (self);
state->dest = g_object_ref (dest);
- state->view = g_object_ref (priv->pan_view);
+ state->page = g_object_ref (priv->pan_page);
state->theatric = priv->pan_theatric;
gtk_widget_translate_coordinates (GTK_WIDGET (dest_priv->top_stack), grid, 0, 0,
@@ -646,8 +647,8 @@ ide_layout_stack_pan_end (IdeLayoutStack *self,
if (dest != self)
{
- g_ptr_array_add (priv->in_transition, g_object_ref (priv->pan_view));
- gtk_container_remove (GTK_CONTAINER (priv->stack), GTK_WIDGET (priv->pan_view));
+ g_ptr_array_add (priv->in_transition, g_object_ref (priv->pan_page));
+ gtk_container_remove (GTK_CONTAINER (priv->stack), GTK_WIDGET (priv->pan_page));
}
IDE_TRACE_MSG ("Animating transition to %s column",
@@ -655,52 +656,52 @@ ide_layout_stack_pan_end (IdeLayoutStack *self,
}
else
{
- g_autoptr(IdeLayoutView) view = g_object_ref (priv->pan_view);
+ g_autoptr(IdePage) page = g_object_ref (priv->pan_page);
- IDE_TRACE_MSG ("Moving view to a previously non-existant column");
+ IDE_TRACE_MSG ("Moving page to a previously non-existant column");
- gtk_container_remove (GTK_CONTAINER (priv->stack), GTK_WIDGET (view));
- gtk_widget_show (GTK_WIDGET (view));
- gtk_container_add (GTK_CONTAINER (dest_priv->stack), GTK_WIDGET (view));
+ gtk_container_remove (GTK_CONTAINER (priv->stack), GTK_WIDGET (page));
+ gtk_widget_show (GTK_WIDGET (page));
+ gtk_container_add (GTK_CONTAINER (dest_priv->stack), GTK_WIDGET (page));
}
cleanup:
g_clear_object (&priv->pan_theatric);
- g_clear_object (&priv->pan_view);
+ g_clear_object (&priv->pan_page);
gtk_widget_queue_draw (gtk_widget_get_toplevel (GTK_WIDGET (self)));
- ide_layout_stack_set_cursor (self, "arrow");
+ ide_frame_set_cursor (self, "arrow");
IDE_EXIT;
}
static void
-ide_layout_stack_constructed (GObject *object)
+ide_frame_constructed (GObject *object)
{
- IdeLayoutStack *self = (IdeLayoutStack *)object;
- IdeLayoutStackPrivate *priv = ide_layout_stack_get_instance_private (self);
+ IdeFrame *self = (IdeFrame *)object;
+ IdeFramePrivate *priv = ide_frame_get_instance_private (self);
- g_assert (IDE_IS_LAYOUT_STACK (self));
+ g_assert (IDE_IS_FRAME (self));
- G_OBJECT_CLASS (ide_layout_stack_parent_class)->constructed (object);
+ G_OBJECT_CLASS (ide_frame_parent_class)->constructed (object);
priv->addins = peas_extension_set_new (peas_engine_get_default (),
- IDE_TYPE_LAYOUT_STACK_ADDIN,
+ IDE_TYPE_FRAME_ADDIN,
NULL);
g_signal_connect (priv->addins,
"extension-added",
- G_CALLBACK (ide_layout_stack_addin_added),
+ G_CALLBACK (ide_frame_addin_added),
self);
g_signal_connect (priv->addins,
"extension-removed",
- G_CALLBACK (ide_layout_stack_addin_removed),
+ G_CALLBACK (ide_frame_addin_removed),
self);
peas_extension_set_foreach (priv->addins,
- ide_layout_stack_addin_added,
+ ide_frame_addin_added,
self);
gtk_widget_add_events (GTK_WIDGET (priv->event_box), GDK_TOUCH_MASK);
@@ -711,15 +712,15 @@ ide_layout_stack_constructed (GObject *object)
NULL);
g_signal_connect_swapped (priv->pan,
"begin",
- G_CALLBACK (ide_layout_stack_pan_begin),
+ G_CALLBACK (ide_frame_pan_begin),
self);
g_signal_connect_swapped (priv->pan,
"update",
- G_CALLBACK (ide_layout_stack_pan_update),
+ G_CALLBACK (ide_frame_pan_update),
self);
g_signal_connect_swapped (priv->pan,
"end",
- G_CALLBACK (ide_layout_stack_pan_end),
+ G_CALLBACK (ide_frame_pan_end),
self);
gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->pan),
GTK_PHASE_BUBBLE);
@@ -736,39 +737,39 @@ ide_layout_stack_constructed (GObject *object)
}
static void
-ide_layout_stack_grab_focus (GtkWidget *widget)
+ide_frame_grab_focus (GtkWidget *widget)
{
- IdeLayoutStack *self = (IdeLayoutStack *)widget;
- IdeLayoutView *child;
+ IdeFrame *self = (IdeFrame *)widget;
+ IdePage *child;
- g_assert (IDE_IS_LAYOUT_STACK (self));
+ g_assert (IDE_IS_FRAME (self));
- child = ide_layout_stack_get_visible_child (self);
+ child = ide_frame_get_visible_child (self);
if (child != NULL)
gtk_widget_grab_focus (GTK_WIDGET (child));
else
- GTK_WIDGET_CLASS (ide_layout_stack_parent_class)->grab_focus (widget);
+ GTK_WIDGET_CLASS (ide_frame_parent_class)->grab_focus (widget);
}
static void
-ide_layout_stack_destroy (GtkWidget *widget)
+ide_frame_destroy (GtkWidget *widget)
{
- IdeLayoutStack *self = (IdeLayoutStack *)widget;
- IdeLayoutStackPrivate *priv = ide_layout_stack_get_instance_private (self);
+ IdeFrame *self = (IdeFrame *)widget;
+ IdeFramePrivate *priv = ide_frame_get_instance_private (self);
- g_assert (IDE_IS_LAYOUT_STACK (self));
+ g_assert (IDE_IS_FRAME (self));
+
+ g_clear_object (&priv->addins);
g_clear_pointer (&priv->in_transition, g_ptr_array_unref);
- if (priv->views != NULL)
+ if (priv->pages != NULL)
{
- g_list_model_items_changed (G_LIST_MODEL (self), 0, priv->views->len, 0);
- g_clear_pointer (&priv->views, g_ptr_array_unref);
+ g_list_model_items_changed (G_LIST_MODEL (self), 0, priv->pages->len, 0);
+ g_clear_pointer (&priv->pages, g_ptr_array_unref);
}
- g_clear_object (&priv->addins);
-
if (priv->bindings != NULL)
{
dzl_binding_group_set_source (priv->bindings, NULL);
@@ -783,25 +784,25 @@ ide_layout_stack_destroy (GtkWidget *widget)
g_clear_object (&priv->pan);
- GTK_WIDGET_CLASS (ide_layout_stack_parent_class)->destroy (widget);
+ GTK_WIDGET_CLASS (ide_frame_parent_class)->destroy (widget);
}
static void
-ide_layout_stack_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
+ide_frame_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
{
- IdeLayoutStack *self = IDE_LAYOUT_STACK (object);
+ IdeFrame *self = IDE_FRAME (object);
switch (prop_id)
{
case PROP_HAS_VIEW:
- g_value_set_boolean (value, ide_layout_stack_get_has_view (self));
+ g_value_set_boolean (value, ide_frame_get_has_page (self));
break;
case PROP_VISIBLE_CHILD:
- g_value_set_object (value, ide_layout_stack_get_visible_child (self));
+ g_value_set_object (value, ide_frame_get_visible_child (self));
break;
default:
@@ -810,17 +811,17 @@ ide_layout_stack_get_property (GObject *object,
}
static void
-ide_layout_stack_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
+ide_frame_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
{
- IdeLayoutStack *self = IDE_LAYOUT_STACK (object);
+ IdeFrame *self = IDE_FRAME (object);
switch (prop_id)
{
case PROP_VISIBLE_CHILD:
- ide_layout_stack_set_visible_child (self, g_value_get_object (value));
+ ide_frame_set_visible_child (self, g_value_get_object (value));
break;
default:
@@ -829,34 +830,34 @@ ide_layout_stack_set_property (GObject *object,
}
static void
-ide_layout_stack_class_init (IdeLayoutStackClass *klass)
+ide_frame_class_init (IdeFrameClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
- object_class->constructed = ide_layout_stack_constructed;
- object_class->get_property = ide_layout_stack_get_property;
- object_class->set_property = ide_layout_stack_set_property;
+ object_class->constructed = ide_frame_constructed;
+ object_class->get_property = ide_frame_get_property;
+ object_class->set_property = ide_frame_set_property;
- widget_class->destroy = ide_layout_stack_destroy;
- widget_class->grab_focus = ide_layout_stack_grab_focus;
+ widget_class->destroy = ide_frame_destroy;
+ widget_class->grab_focus = ide_frame_grab_focus;
- container_class->add = ide_layout_stack_add;
+ container_class->add = ide_frame_add;
- klass->agree_to_close_async = ide_layout_stack_real_agree_to_close_async;
- klass->agree_to_close_finish = ide_layout_stack_real_agree_to_close_finish;
+ klass->agree_to_close_async = ide_frame_real_agree_to_close_async;
+ klass->agree_to_close_finish = ide_frame_real_agree_to_close_finish;
properties [PROP_HAS_VIEW] =
- g_param_spec_boolean ("has-view", NULL, NULL,
+ g_param_spec_boolean ("has-page", NULL, NULL,
FALSE,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_VISIBLE_CHILD] =
g_param_spec_object ("visible-child",
"Visible Child",
- "The current view to be displayed",
- IDE_TYPE_LAYOUT_VIEW,
+ "The current page to be displayed",
+ IDE_TYPE_PAGE,
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
@@ -865,50 +866,50 @@ ide_layout_stack_class_init (IdeLayoutStackClass *klass)
g_signal_new_class_handler ("change-current-page",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_CALLBACK (ide_layout_stack_change_current_page),
+ G_CALLBACK (ide_frame_change_current_page),
NULL, NULL,
g_cclosure_marshal_VOID__INT,
G_TYPE_NONE, 1, G_TYPE_INT);
- gtk_widget_class_set_css_name (widget_class, "idelayoutstack");
- gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/builder/ui/ide-layout-stack.ui");
- gtk_widget_class_bind_template_child_private (widget_class, IdeLayoutStack, empty_state);
- gtk_widget_class_bind_template_child_private (widget_class, IdeLayoutStack, failed_state);
- gtk_widget_class_bind_template_child_private (widget_class, IdeLayoutStack, header);
- gtk_widget_class_bind_template_child_private (widget_class, IdeLayoutStack, stack);
- gtk_widget_class_bind_template_child_private (widget_class, IdeLayoutStack, top_stack);
- gtk_widget_class_bind_template_child_private (widget_class, IdeLayoutStack, event_box);
-
- g_type_ensure (IDE_TYPE_LAYOUT_STACK_HEADER);
- g_type_ensure (IDE_TYPE_LAYOUT_STACK_WRAPPER);
+ gtk_widget_class_set_css_name (widget_class, "ideframe");
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/libide-gui/ui/ide-frame.ui");
+ gtk_widget_class_bind_template_child_private (widget_class, IdeFrame, empty_state);
+ gtk_widget_class_bind_template_child_private (widget_class, IdeFrame, failed_state);
+ gtk_widget_class_bind_template_child_private (widget_class, IdeFrame, header);
+ gtk_widget_class_bind_template_child_private (widget_class, IdeFrame, stack);
+ gtk_widget_class_bind_template_child_private (widget_class, IdeFrame, top_stack);
+ gtk_widget_class_bind_template_child_private (widget_class, IdeFrame, event_box);
+
+ g_type_ensure (IDE_TYPE_FRAME_HEADER);
+ g_type_ensure (IDE_TYPE_FRAME_WRAPPER);
g_type_ensure (IDE_TYPE_SHORTCUT_LABEL);
}
static void
-ide_layout_stack_init (IdeLayoutStack *self)
+ide_frame_init (IdeFrame *self)
{
- IdeLayoutStackPrivate *priv = ide_layout_stack_get_instance_private (self);
+ IdeFramePrivate *priv = ide_frame_get_instance_private (self);
gtk_widget_init_template (GTK_WIDGET (self));
- _ide_layout_stack_init_actions (self);
- _ide_layout_stack_init_shortcuts (self);
+ _ide_frame_init_actions (self);
+ _ide_frame_init_shortcuts (self);
- priv->views = g_ptr_array_new ();
+ priv->pages = g_ptr_array_new ();
priv->in_transition = g_ptr_array_new_with_free_func (g_object_unref);
- priv->signals = dzl_signal_group_new (IDE_TYPE_LAYOUT_VIEW);
+ priv->signals = dzl_signal_group_new (IDE_TYPE_PAGE);
dzl_signal_group_connect_swapped (priv->signals,
"notify::failed",
- G_CALLBACK (ide_layout_stack_view_failed),
+ G_CALLBACK (ide_frame_page_failed),
self);
priv->bindings = dzl_binding_group_new ();
g_signal_connect_object (priv->bindings,
"notify::source",
- G_CALLBACK (ide_layout_stack_bindings_notify_source),
+ G_CALLBACK (ide_frame_bindings_notify_source),
self,
G_CONNECT_SWAPPED);
@@ -930,135 +931,135 @@ ide_layout_stack_init (IdeLayoutStack *self)
g_signal_connect_object (priv->stack,
"notify::visible-child",
- G_CALLBACK (ide_layout_stack_notify_visible_child),
+ G_CALLBACK (ide_frame_notify_visible_child),
self,
G_CONNECT_SWAPPED);
g_signal_connect_object (priv->stack,
"add",
- G_CALLBACK (ide_layout_stack_view_added),
+ G_CALLBACK (ide_frame_page_added),
self,
G_CONNECT_SWAPPED | G_CONNECT_AFTER);
g_signal_connect_object (priv->stack,
"remove",
- G_CALLBACK (ide_layout_stack_view_removed),
+ G_CALLBACK (ide_frame_page_removed),
self,
G_CONNECT_SWAPPED);
- _ide_layout_stack_header_set_views (priv->header, G_LIST_MODEL (self));
- _ide_layout_stack_header_update (priv->header, NULL);
+ _ide_frame_header_set_pages (priv->header, G_LIST_MODEL (self));
+ _ide_frame_header_update (priv->header, NULL);
}
GtkWidget *
-ide_layout_stack_new (void)
+ide_frame_new (void)
{
- return g_object_new (IDE_TYPE_LAYOUT_STACK, NULL);
+ return g_object_new (IDE_TYPE_FRAME, NULL);
}
/**
- * ide_layout_stack_set_visible_child:
- * @self: a #IdeLayoutStack
+ * ide_frame_set_visible_child:
+ * @self: a #IdeFrame
*
- * Sets the current view for the stack.
+ * Sets the current page for the stack.
*
* Since: 3.32
*/
void
-ide_layout_stack_set_visible_child (IdeLayoutStack *self,
- IdeLayoutView *view)
+ide_frame_set_visible_child (IdeFrame *self,
+ IdePage *page)
{
- IdeLayoutStackPrivate *priv = ide_layout_stack_get_instance_private (self);
+ IdeFramePrivate *priv = ide_frame_get_instance_private (self);
- g_return_if_fail (IDE_IS_LAYOUT_STACK (self));
- g_return_if_fail (IDE_IS_LAYOUT_VIEW (view));
- g_return_if_fail (gtk_widget_get_parent (GTK_WIDGET (view)) == (GtkWidget *)priv->stack);
+ g_return_if_fail (IDE_IS_FRAME (self));
+ g_return_if_fail (IDE_IS_PAGE (page));
+ g_return_if_fail (gtk_widget_get_parent (GTK_WIDGET (page)) == (GtkWidget *)priv->stack);
- gtk_stack_set_visible_child (priv->stack, GTK_WIDGET (view));
+ gtk_stack_set_visible_child (priv->stack, GTK_WIDGET (page));
}
/**
- * ide_layout_stack_get_visible_child:
- * @self: a #IdeLayoutStack
+ * ide_frame_get_visible_child:
+ * @self: a #IdeFrame
*
- * Gets the visible #IdeLayoutView if there is one; otherwise %NULL.
+ * Gets the visible #IdePage if there is one; otherwise %NULL.
*
- * Returns: (nullable) (transfer none): An #IdeLayoutView or %NULL
+ * Returns: (nullable) (transfer none): An #IdePage or %NULL
*
* Since: 3.32
*/
-IdeLayoutView *
-ide_layout_stack_get_visible_child (IdeLayoutStack *self)
+IdePage *
+ide_frame_get_visible_child (IdeFrame *self)
{
- IdeLayoutStackPrivate *priv = ide_layout_stack_get_instance_private (self);
+ IdeFramePrivate *priv = ide_frame_get_instance_private (self);
- g_return_val_if_fail (IDE_IS_LAYOUT_STACK (self), NULL);
+ g_return_val_if_fail (IDE_IS_FRAME (self), NULL);
- return IDE_LAYOUT_VIEW (gtk_stack_get_visible_child (priv->stack));
+ return IDE_PAGE (gtk_stack_get_visible_child (priv->stack));
}
/**
- * ide_layout_stack_get_titlebar:
- * @self: a #IdeLayoutStack
+ * ide_frame_get_titlebar:
+ * @self: a #IdeFrame
*
- * Gets the #IdeLayoutStackHeader header that is at the top of the stack.
+ * Gets the #IdeFrameHeader header that is at the top of the stack.
*
- * Returns: (transfer none) (type Ide.LayoutStackHeader): The layout stack header.
+ * Returns: (transfer none) (type IdeFrameHeader): The layout stack header.
*
* Since: 3.32
*/
GtkWidget *
-ide_layout_stack_get_titlebar (IdeLayoutStack *self)
+ide_frame_get_titlebar (IdeFrame *self)
{
- IdeLayoutStackPrivate *priv = ide_layout_stack_get_instance_private (self);
+ IdeFramePrivate *priv = ide_frame_get_instance_private (self);
- g_return_val_if_fail (IDE_IS_LAYOUT_STACK (self), NULL);
+ g_return_val_if_fail (IDE_IS_FRAME (self), NULL);
return GTK_WIDGET (priv->header);
}
/**
- * ide_layout_stack_get_has_view:
- * @self: an #IdeLayoutStack
+ * ide_frame_get_has_page:
+ * @self: an #IdeFrame
*
- * Gets the "has-view" property.
+ * Gets the "has-page" property.
*
* This property is a convenience to allow widgets to easily bind
- * properties based on whether or not a view is visible in the stack.
+ * properties based on whether or not a page is visible in the stack.
*
- * Returns: %TRUE if the stack has a view
+ * Returns: %TRUE if the stack has a page
*
* Since: 3.32
*/
gboolean
-ide_layout_stack_get_has_view (IdeLayoutStack *self)
+ide_frame_get_has_page (IdeFrame *self)
{
- IdeLayoutView *visible_child;
+ IdePage *visible_child;
- g_return_val_if_fail (IDE_IS_LAYOUT_STACK (self), FALSE);
+ g_return_val_if_fail (IDE_IS_FRAME (self), FALSE);
- visible_child = ide_layout_stack_get_visible_child (self);
+ visible_child = ide_frame_get_visible_child (self);
return visible_child != NULL;
}
static void
-ide_layout_stack_close_view_cb (GObject *object,
- GAsyncResult *result,
- gpointer user_data)
+ide_frame_close_page_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
{
- IdeLayoutView *view = (IdeLayoutView *)object;
- g_autoptr(IdeLayoutStack) self = user_data;
+ IdePage *page = (IdePage *)object;
+ g_autoptr(IdeFrame) self = user_data;
g_autoptr(GError) error = NULL;
GtkWidget *toplevel;
GtkWidget *focus;
gboolean had_focus = FALSE;
- g_assert (IDE_IS_LAYOUT_VIEW (view));
+ g_assert (IDE_IS_PAGE (page));
g_assert (G_IS_ASYNC_RESULT (result));
- g_assert (IDE_IS_LAYOUT_STACK (self));
+ g_assert (IDE_IS_FRAME (self));
- if (!ide_layout_view_agree_to_close_finish (view, result, &error))
+ if (!ide_page_agree_to_close_finish (page, result, &error))
{
g_message ("%s", error->message);
return;
@@ -1067,15 +1068,15 @@ ide_layout_stack_close_view_cb (GObject *object,
/* Keep track of whether or not the widget had focus (which
* would happen if we were activated from a keybinding.
*/
- toplevel = gtk_widget_get_toplevel (GTK_WIDGET (view));
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (page));
if (GTK_IS_WINDOW (toplevel) &&
NULL != (focus = gtk_window_get_focus (GTK_WINDOW (toplevel))) &&
- (focus == GTK_WIDGET (view) ||
- gtk_widget_is_ancestor (focus, GTK_WIDGET (view))))
+ (focus == GTK_WIDGET (page) ||
+ gtk_widget_is_ancestor (focus, GTK_WIDGET (page))))
had_focus = TRUE;
/* Now we can destroy the child */
- gtk_widget_destroy (GTK_WIDGET (view));
+ gtk_widget_destroy (GTK_WIDGET (page));
/* We don't want to leave the widget focus in an indeterminate
* state so we immediately focus the next child in the stack.
@@ -1083,7 +1084,7 @@ ide_layout_stack_close_view_cb (GObject *object,
*/
if (had_focus)
{
- IdeLayoutView *visible_child = ide_layout_stack_get_visible_child (self);
+ IdePage *visible_child = ide_frame_get_visible_child (self);
if (visible_child != NULL)
gtk_widget_grab_focus (GTK_WIDGET (visible_child));
@@ -1091,108 +1092,108 @@ ide_layout_stack_close_view_cb (GObject *object,
}
void
-_ide_layout_stack_request_close (IdeLayoutStack *self,
- IdeLayoutView *view)
+_ide_frame_request_close (IdeFrame *self,
+ IdePage *page)
{
- g_return_if_fail (IDE_IS_LAYOUT_STACK (self));
- g_return_if_fail (IDE_IS_LAYOUT_VIEW (view));
+ g_return_if_fail (IDE_IS_FRAME (self));
+ g_return_if_fail (IDE_IS_PAGE (page));
- ide_layout_view_agree_to_close_async (view,
+ ide_page_agree_to_close_async (page,
NULL,
- ide_layout_stack_close_view_cb,
+ ide_frame_close_page_cb,
g_object_ref (self));
}
static GType
-ide_layout_stack_get_item_type (GListModel *model)
+ide_frame_get_item_type (GListModel *model)
{
- return IDE_TYPE_LAYOUT_VIEW;
+ return IDE_TYPE_PAGE;
}
static guint
-ide_layout_stack_get_n_items (GListModel *model)
+ide_frame_get_n_items (GListModel *model)
{
- IdeLayoutStack *self = (IdeLayoutStack *)model;
- IdeLayoutStackPrivate *priv = ide_layout_stack_get_instance_private (self);
+ IdeFrame *self = (IdeFrame *)model;
+ IdeFramePrivate *priv = ide_frame_get_instance_private (self);
- g_assert (IDE_IS_LAYOUT_STACK (self));
+ g_assert (IDE_IS_FRAME (self));
- return priv->views ? priv->views->len : 0;
+ return priv->pages ? priv->pages->len : 0;
}
static gpointer
-ide_layout_stack_get_item (GListModel *model,
- guint position)
+ide_frame_get_item (GListModel *model,
+ guint position)
{
- IdeLayoutStack *self = (IdeLayoutStack *)model;
- IdeLayoutStackPrivate *priv = ide_layout_stack_get_instance_private (self);
+ IdeFrame *self = (IdeFrame *)model;
+ IdeFramePrivate *priv = ide_frame_get_instance_private (self);
- g_assert (IDE_IS_LAYOUT_STACK (self));
- g_assert (position < priv->views->len);
+ g_assert (IDE_IS_FRAME (self));
+ g_assert (position < priv->pages->len);
- return g_object_ref (g_ptr_array_index (priv->views, position));
+ return g_object_ref (g_ptr_array_index (priv->pages, position));
}
static void
list_model_iface_init (GListModelInterface *iface)
{
- iface->get_n_items = ide_layout_stack_get_n_items;
- iface->get_item = ide_layout_stack_get_item;
- iface->get_item_type = ide_layout_stack_get_item_type;
+ iface->get_n_items = ide_frame_get_n_items;
+ iface->get_item = ide_frame_get_item;
+ iface->get_item_type = ide_frame_get_item_type;
}
void
-ide_layout_stack_agree_to_close_async (IdeLayoutStack *self,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+ide_frame_agree_to_close_async (IdeFrame *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- g_return_if_fail (IDE_IS_LAYOUT_STACK (self));
+ g_return_if_fail (IDE_IS_FRAME (self));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
- IDE_LAYOUT_STACK_GET_CLASS (self)->agree_to_close_async (self, cancellable, callback, user_data);
+ IDE_FRAME_GET_CLASS (self)->agree_to_close_async (self, cancellable, callback, user_data);
}
gboolean
-ide_layout_stack_agree_to_close_finish (IdeLayoutStack *self,
- GAsyncResult *result,
- GError **error)
+ide_frame_agree_to_close_finish (IdeFrame *self,
+ GAsyncResult *result,
+ GError **error)
{
- g_return_val_if_fail (IDE_IS_LAYOUT_STACK (self), FALSE);
+ g_return_val_if_fail (IDE_IS_FRAME (self), FALSE);
g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
- return IDE_LAYOUT_STACK_GET_CLASS (self)->agree_to_close_finish (self, result, error);
+ return IDE_FRAME_GET_CLASS (self)->agree_to_close_finish (self, result, error);
}
static void
animation_state_complete (gpointer data)
{
- IdeLayoutStackPrivate *priv;
+ IdeFramePrivate *priv;
AnimationState *state = data;
g_assert (state != NULL);
- g_assert (IDE_IS_LAYOUT_STACK (state->source));
- g_assert (IDE_IS_LAYOUT_STACK (state->dest));
- g_assert (IDE_IS_LAYOUT_VIEW (state->view));
+ g_assert (IDE_IS_FRAME (state->source));
+ g_assert (IDE_IS_FRAME (state->dest));
+ g_assert (IDE_IS_PAGE (state->page));
/* Add the widget to the new stack */
if (state->dest != state->source)
{
- gtk_container_add (GTK_CONTAINER (state->dest), GTK_WIDGET (state->view));
+ gtk_container_add (GTK_CONTAINER (state->dest), GTK_WIDGET (state->page));
/* Now remove it from our temporary transition. Be careful in case we were
* destroyed in the mean time.
*/
- priv = ide_layout_stack_get_instance_private (state->source);
+ priv = ide_frame_get_instance_private (state->source);
if (priv->in_transition != NULL)
{
guint position = 0;
- if (g_ptr_array_find_with_equal_func (priv->views, state->view, NULL, &position))
+ if (g_ptr_array_find_with_equal_func (priv->pages, state->page, NULL, &position))
{
- g_ptr_array_remove (priv->in_transition, state->view);
- g_ptr_array_remove_index (priv->views, position);
+ g_ptr_array_remove (priv->in_transition, state->page);
+ g_ptr_array_remove_index (priv->pages, position);
g_list_model_items_changed (G_LIST_MODEL (state->source), position, 1, 0);
}
}
@@ -1200,33 +1201,33 @@ animation_state_complete (gpointer data)
/*
* We might need to reshow the widget in cases where we are in a
- * three-finger-swipe of the view. There is also a chance that we
+ * three-finger-swipe of the page. There is also a chance that we
* aren't the proper visible child and that needs to be restored now.
*/
- gtk_widget_show (GTK_WIDGET (state->view));
- ide_layout_stack_set_visible_child (state->dest, state->view);
+ gtk_widget_show (GTK_WIDGET (state->page));
+ ide_frame_set_visible_child (state->dest, state->page);
g_clear_object (&state->source);
g_clear_object (&state->dest);
- g_clear_object (&state->view);
+ g_clear_object (&state->page);
g_clear_object (&state->theatric);
g_slice_free (AnimationState, state);
}
void
-_ide_layout_stack_transfer (IdeLayoutStack *self,
- IdeLayoutStack *dest,
- IdeLayoutView *view)
+_ide_frame_transfer (IdeFrame *self,
+ IdeFrame *dest,
+ IdePage *page)
{
- IdeLayoutStackPrivate *priv = ide_layout_stack_get_instance_private (self);
- IdeLayoutStackPrivate *dest_priv = ide_layout_stack_get_instance_private (dest);
+ IdeFramePrivate *priv = ide_frame_get_instance_private (self);
+ IdeFramePrivate *dest_priv = ide_frame_get_instance_private (dest);
const GdkRGBA *fg;
const GdkRGBA *bg;
- g_return_if_fail (IDE_IS_LAYOUT_STACK (self));
- g_return_if_fail (IDE_IS_LAYOUT_STACK (dest));
- g_return_if_fail (IDE_IS_LAYOUT_VIEW (view));
- g_return_if_fail (GTK_WIDGET (priv->stack) == gtk_widget_get_parent (GTK_WIDGET (view)));
+ g_return_if_fail (IDE_IS_FRAME (self));
+ g_return_if_fail (IDE_IS_FRAME (dest));
+ g_return_if_fail (IDE_IS_PAGE (page));
+ g_return_if_fail (GTK_WIDGET (priv->stack) == gtk_widget_get_parent (GTK_WIDGET (page)));
/*
* Inform the destination stack about our new primary colors so that it can
@@ -1235,20 +1236,20 @@ _ide_layout_stack_transfer (IdeLayoutStack *self,
* transitions.
*/
- fg = ide_layout_view_get_primary_color_fg (view);
- bg = ide_layout_view_get_primary_color_bg (view);
- _ide_layout_stack_header_set_foreground_rgba (dest_priv->header, fg);
- _ide_layout_stack_header_set_background_rgba (dest_priv->header, bg);
+ fg = ide_page_get_primary_color_fg (page);
+ bg = ide_page_get_primary_color_bg (page);
+ _ide_frame_header_set_foreground_rgba (dest_priv->header, fg);
+ _ide_frame_header_set_background_rgba (dest_priv->header, bg);
/*
* If both the old and the new stacks are mapped, we can animate
- * between them using a snapshot of the view. Well, we also need
+ * between them using a snapshot of the page. Well, we also need
* to be sure they have a valid allocation, but that check is done
* slightly after this because it makes things easier.
*/
if (gtk_widget_get_mapped (GTK_WIDGET (self)) &&
gtk_widget_get_mapped (GTK_WIDGET (dest)) &&
- gtk_widget_get_mapped (GTK_WIDGET (view)))
+ gtk_widget_get_mapped (GTK_WIDGET (page)))
{
GtkAllocation alloc, dest_alloc;
cairo_surface_t *surface = NULL;
@@ -1256,9 +1257,9 @@ _ide_layout_stack_transfer (IdeLayoutStack *self,
GtkWidget *grid;
gboolean enable_animations;
- grid = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_LAYOUT_GRID);
+ grid = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_GRID);
- gtk_widget_get_allocation (GTK_WIDGET (view), &alloc);
+ gtk_widget_get_allocation (GTK_WIDGET (page), &alloc);
gtk_widget_get_allocation (GTK_WIDGET (dest), &dest_alloc);
g_object_get (gtk_settings_get_default (),
@@ -1270,7 +1271,7 @@ _ide_layout_stack_transfer (IdeLayoutStack *self,
!is_uninitialized (&alloc) &&
!is_uninitialized (&dest_alloc) &&
dest_alloc.width > 0 && dest_alloc.height > 0 &&
- NULL != (window = gtk_widget_get_window (GTK_WIDGET (view))) &&
+ NULL != (window = gtk_widget_get_window (GTK_WIDGET (page))) &&
NULL != (surface = gdk_window_create_similar_surface (window,
CAIRO_CONTENT_COLOR,
alloc.width,
@@ -1281,7 +1282,7 @@ _ide_layout_stack_transfer (IdeLayoutStack *self,
cairo_t *cr;
cr = cairo_create (surface);
- gtk_widget_draw (GTK_WIDGET (view), cr);
+ gtk_widget_draw (GTK_WIDGET (page), cr);
cairo_destroy (cr);
gtk_widget_translate_coordinates (GTK_WIDGET (priv->stack), grid, 0, 0,
@@ -1301,7 +1302,7 @@ _ide_layout_stack_transfer (IdeLayoutStack *self,
state = g_slice_new0 (AnimationState);
state->source = g_object_ref (self);
state->dest = g_object_ref (dest);
- state->view = g_object_ref (view);
+ state->page = g_object_ref (page);
state->theatric = theatric;
dzl_object_animate_full (theatric,
@@ -1317,11 +1318,11 @@ _ide_layout_stack_transfer (IdeLayoutStack *self,
NULL);
/*
- * Mark the view as in-transition so that when we remove it
+ * Mark the page as in-transition so that when we remove it
* we can ignore the items-changed until the animation completes.
*/
- g_ptr_array_add (priv->in_transition, g_object_ref (view));
- gtk_container_remove (GTK_CONTAINER (priv->stack), GTK_WIDGET (view));
+ g_ptr_array_add (priv->in_transition, g_object_ref (page));
+ gtk_container_remove (GTK_CONTAINER (priv->stack), GTK_WIDGET (page));
cairo_surface_destroy (surface);
@@ -1329,60 +1330,60 @@ _ide_layout_stack_transfer (IdeLayoutStack *self,
}
}
- g_object_ref (view);
- gtk_container_remove (GTK_CONTAINER (priv->stack), GTK_WIDGET (view));
- gtk_container_add (GTK_CONTAINER (dest_priv->stack), GTK_WIDGET (view));
- g_object_unref (view);
+ g_object_ref (page);
+ gtk_container_remove (GTK_CONTAINER (priv->stack), GTK_WIDGET (page));
+ gtk_container_add (GTK_CONTAINER (dest_priv->stack), GTK_WIDGET (page));
+ g_object_unref (page);
}
/**
- * ide_layout_stack_foreach_view:
- * @self: a #IdeLayoutStack
- * @callback: (scope call) (closure user_data): A callback for each view
+ * ide_frame_foreach_page:
+ * @self: a #IdeFrame
+ * @callback: (scope call) (closure user_data): A callback for each page
* @user_data: user data for @callback
*
- * This function will call @callback for every view found in @self.
+ * This function will call @callback for every page found in @self.
*
* Since: 3.32
*/
void
-ide_layout_stack_foreach_view (IdeLayoutStack *self,
- GtkCallback callback,
- gpointer user_data)
+ide_frame_foreach_page (IdeFrame *self,
+ GtkCallback callback,
+ gpointer user_data)
{
- IdeLayoutStackPrivate *priv = ide_layout_stack_get_instance_private (self);
+ IdeFramePrivate *priv = ide_frame_get_instance_private (self);
- g_return_if_fail (IDE_IS_LAYOUT_STACK (self));
+ g_return_if_fail (IDE_IS_FRAME (self));
g_return_if_fail (callback != NULL);
gtk_container_foreach (GTK_CONTAINER (priv->stack), callback, user_data);
}
/**
- * ide_layout_stack_addin_find_by_module_name:
- * @stack: An #IdeLayoutStack
+ * ide_frame_addin_find_by_module_name:
+ * @frame: An #IdeFrame
* @module_name: the module name which provides the addin
*
- * This function will locate the #IdeLayoutStackAddin that was registered by
+ * This function will locate the #IdeFrameAddin that was registered by
* the plugin named @module_name (which should match the "Module" field
* provided in the .plugin file).
*
* If no module was found or that module does not implement the
- * #IdeLayoutStackAddinInterface, then %NULL is returned.
+ * #IdeFrameAddinInterface, then %NULL is returned.
*
- * Returns: (transfer none) (nullable): An #IdeLayoutStackAddin or %NULL
+ * Returns: (transfer none) (nullable): An #IdeFrameAddin or %NULL
*
* Since: 3.32
*/
-IdeLayoutStackAddin *
-ide_layout_stack_addin_find_by_module_name (IdeLayoutStack *stack,
- const gchar *module_name)
+IdeFrameAddin *
+ide_frame_addin_find_by_module_name (IdeFrame *frame,
+ const gchar *module_name)
{
- IdeLayoutStackPrivate *priv = ide_layout_stack_get_instance_private (stack);
+ IdeFramePrivate *priv = ide_frame_get_instance_private (frame);
PeasExtension *ret = NULL;
PeasPluginInfo *plugin_info;
- g_return_val_if_fail (IDE_IS_LAYOUT_STACK (stack), NULL);
+ g_return_val_if_fail (IDE_IS_FRAME (frame), NULL);
g_return_val_if_fail (priv->addins != NULL, NULL);
g_return_val_if_fail (module_name != NULL, NULL);
@@ -1393,17 +1394,17 @@ ide_layout_stack_addin_find_by_module_name (IdeLayoutStack *stack,
else
g_warning ("No addin could be found matching module \"%s\"", module_name);
- return ret ? IDE_LAYOUT_STACK_ADDIN (ret) : NULL;
+ return ret ? IDE_FRAME_ADDIN (ret) : NULL;
}
void
-ide_layout_stack_add_with_depth (IdeLayoutStack *self,
- GtkWidget *widget,
- guint position)
+ide_frame_add_with_depth (IdeFrame *self,
+ GtkWidget *widget,
+ guint position)
{
- IdeLayoutStackPrivate *priv = ide_layout_stack_get_instance_private (self);
+ IdeFramePrivate *priv = ide_frame_get_instance_private (self);
- g_return_if_fail (IDE_IS_LAYOUT_STACK (self));
+ g_return_if_fail (IDE_IS_FRAME (self));
g_return_if_fail (GTK_IS_WIDGET (widget));
gtk_container_add_with_properties (GTK_CONTAINER (priv->stack), widget,
diff --git a/src/libide/gui/ide-frame.h b/src/libide/gui/ide-frame.h
new file mode 100644
index 000000000..36e586d7c
--- /dev/null
+++ b/src/libide/gui/ide-frame.h
@@ -0,0 +1,84 @@
+/* ide-frame.h
+ *
+ * Copyright 2017-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#if !defined (IDE_GUI_INSIDE) && !defined (IDE_GUI_COMPILATION)
+# error "Only <libide-gui.h> can be included directly."
+#endif
+
+#include <gtk/gtk.h>
+#include <libide-core.h>
+
+#include "ide-page.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_FRAME (ide_frame_get_type())
+
+IDE_AVAILABLE_IN_3_32
+G_DECLARE_DERIVABLE_TYPE (IdeFrame, ide_frame, IDE, FRAME, GtkBox)
+
+struct _IdeFrameClass
+{
+ GtkBoxClass parent_class;
+
+ void (*agree_to_close_async) (IdeFrame *stack,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (*agree_to_close_finish) (IdeFrame *stack,
+ GAsyncResult *result,
+ GError **error);
+
+ /*< private >*/
+ gpointer _reserved[16];
+};
+
+IDE_AVAILABLE_IN_3_32
+GtkWidget *ide_frame_new (void);
+IDE_AVAILABLE_IN_3_32
+GtkWidget *ide_frame_get_titlebar (IdeFrame *self);
+IDE_AVAILABLE_IN_3_32
+IdePage *ide_frame_get_visible_child (IdeFrame *self);
+IDE_AVAILABLE_IN_3_32
+void ide_frame_set_visible_child (IdeFrame *self,
+ IdePage *page);
+IDE_AVAILABLE_IN_3_32
+gboolean ide_frame_get_has_page (IdeFrame *self);
+IDE_AVAILABLE_IN_3_32
+void ide_frame_agree_to_close_async (IdeFrame *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+IDE_AVAILABLE_IN_3_32
+gboolean ide_frame_agree_to_close_finish (IdeFrame *self,
+ GAsyncResult *result,
+ GError **error);
+IDE_AVAILABLE_IN_3_32
+void ide_frame_foreach_page (IdeFrame *self,
+ GtkCallback callback,
+ gpointer user_data);
+IDE_AVAILABLE_IN_3_32
+void ide_frame_add_with_depth (IdeFrame *self,
+ GtkWidget *widget,
+ guint position);
+
+G_END_DECLS
diff --git a/src/libide/layout/ide-layout-stack.ui b/src/libide/gui/ide-frame.ui
similarity index 97%
rename from src/libide/layout/ide-layout-stack.ui
rename to src/libide/gui/ide-frame.ui
index fa7cf2d70..3dc728752 100644
--- a/src/libide/layout/ide-layout-stack.ui
+++ b/src/libide/gui/ide-frame.ui
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
- <template class="IdeLayoutStack" parent="GtkBox">
+ <template class="IdeFrame" parent="GtkBox">
<property name="orientation">vertical</property>
<child>
- <object class="IdeLayoutStackHeader" id="header">
+ <object class="IdeFrameHeader" id="header">
<property name="show-close-button">true</property>
<property name="title" translatable="yes">No Open Pages</property>
<property name="visible">true</property>
@@ -127,7 +127,7 @@
</object>
</child>
<child>
- <object class="IdeLayoutStackWrapper" id="stack">
+ <object class="IdeFrameWrapper" id="stack">
<property name="expand">true</property>
<property name="homogeneous">false</property>
<property name="interpolate-size">false</property>
diff --git a/src/libide/layout/ide-layout-grid-actions.c b/src/libide/gui/ide-grid-actions.c
similarity index 73%
rename from src/libide/layout/ide-layout-grid-actions.c
rename to src/libide/gui/ide-grid-actions.c
index 1d14d24e2..472f9e4d8 100644
--- a/src/libide/layout/ide-layout-grid-actions.c
+++ b/src/libide/gui/ide-grid-actions.c
@@ -1,4 +1,4 @@
-/* ide-layout-grid-actions.c
+/* ide-grid-actions.c
*
* Copyright 2017-2019 Christian Hergert <chergert redhat com>
*
@@ -18,25 +18,25 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
-#define G_LOG_DOMAIN "ide-layout-grid"
+#define G_LOG_DOMAIN "ide-grid"
#include "config.h"
-#include "layout/ide-layout-grid.h"
-#include "layout/ide-layout-private.h"
+#include "ide-grid.h"
+#include "ide-gui-private.h"
static void
-ide_layout_grid_actions_focus_neighbor (GSimpleAction *action,
+ide_grid_actions_focus_neighbor (GSimpleAction *action,
GVariant *variant,
gpointer user_data)
{
- IdeLayoutGrid *self = user_data;
+ IdeGrid *self = user_data;
GtkDirectionType dir;
g_return_if_fail (G_IS_SIMPLE_ACTION (action));
g_return_if_fail (variant != NULL);
g_return_if_fail (g_variant_is_of_type (variant, G_VARIANT_TYPE_INT32));
- g_return_if_fail (IDE_IS_LAYOUT_GRID (self));
+ g_return_if_fail (IDE_IS_GRID (self));
dir = (GtkDirectionType)g_variant_get_int32 (variant);
@@ -48,7 +48,7 @@ ide_layout_grid_actions_focus_neighbor (GSimpleAction *action,
case GTK_DIR_DOWN:
case GTK_DIR_LEFT:
case GTK_DIR_RIGHT:
- ide_layout_grid_focus_neighbor (self, dir);
+ ide_grid_focus_neighbor (self, dir);
break;
default:
@@ -57,17 +57,17 @@ ide_layout_grid_actions_focus_neighbor (GSimpleAction *action,
}
static const GActionEntry actions[] = {
- { "focus-neighbor", ide_layout_grid_actions_focus_neighbor, "i" },
+ { "focus-neighbor", ide_grid_actions_focus_neighbor, "i" },
};
void
-_ide_layout_grid_init_actions (IdeLayoutGrid *self)
+_ide_grid_init_actions (IdeGrid *self)
{
g_autoptr(GSimpleActionGroup) group = NULL;
- g_return_if_fail (IDE_IS_LAYOUT_GRID (self));
+ g_return_if_fail (IDE_IS_GRID (self));
group = g_simple_action_group_new ();
g_action_map_add_action_entries (G_ACTION_MAP (group), actions, G_N_ELEMENTS (actions), self);
- gtk_widget_insert_action_group (GTK_WIDGET (self), "layoutgrid", G_ACTION_GROUP (group));
+ gtk_widget_insert_action_group (GTK_WIDGET (self), "grid", G_ACTION_GROUP (group));
}
diff --git a/src/libide/layout/ide-layout-grid-column-actions.c b/src/libide/gui/ide-grid-column-actions.c
similarity index 65%
rename from src/libide/layout/ide-layout-grid-column-actions.c
rename to src/libide/gui/ide-grid-column-actions.c
index 5dd83e66c..54786ae8b 100644
--- a/src/libide/layout/ide-layout-grid-column-actions.c
+++ b/src/libide/gui/ide-grid-column-actions.c
@@ -1,4 +1,4 @@
-/* ide-layout-grid-column-actions.c
+/* ide-grid-column-actions.c
*
* Copyright 2017-2019 Christian Hergert <chergert redhat com>
*
@@ -18,57 +18,57 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
-#define G_LOG_DOMAIN "ide-layout-grid-column-actions"
+#define G_LOG_DOMAIN "ide-grid-column-actions"
#include "config.h"
-#include "layout/ide-layout-private.h"
+#include "ide-gui-private.h"
static void
-ide_layout_grid_column_actions_close (GSimpleAction *action,
- GVariant *variant,
- gpointer user_data)
+ide_grid_column_actions_close (GSimpleAction *action,
+ GVariant *variant,
+ gpointer user_data)
{
- IdeLayoutGridColumn *self = user_data;
+ IdeGridColumn *self = user_data;
g_assert (G_IS_SIMPLE_ACTION (action));
- g_assert (IDE_IS_LAYOUT_GRID_COLUMN (self));
+ g_assert (IDE_IS_GRID_COLUMN (self));
- _ide_layout_grid_column_try_close (self);
+ _ide_grid_column_try_close (self);
}
static const GActionEntry grid_column_actions[] = {
- { "close", ide_layout_grid_column_actions_close },
+ { "close", ide_grid_column_actions_close },
};
void
-_ide_layout_grid_column_update_actions (IdeLayoutGridColumn *self)
+_ide_grid_column_update_actions (IdeGridColumn *self)
{
GtkWidget *grid;
gboolean can_close;
- g_assert (IDE_IS_LAYOUT_GRID_COLUMN (self));
+ g_assert (IDE_IS_GRID_COLUMN (self));
grid = gtk_widget_get_parent (GTK_WIDGET (self));
- if (grid == NULL || !IDE_IS_LAYOUT_GRID (grid))
+ if (grid == NULL || !IDE_IS_GRID (grid))
{
g_warning ("Attempt to update actions in unowned grid column");
return;
}
can_close = (dzl_multi_paned_get_n_children (DZL_MULTI_PANED (grid)) > 1);
- dzl_gtk_widget_action_set (GTK_WIDGET (self), "layoutgridcolumn", "close",
+ dzl_gtk_widget_action_set (GTK_WIDGET (self), "gridcolumn", "close",
"enabled", can_close,
NULL);
}
void
-_ide_layout_grid_column_init_actions (IdeLayoutGridColumn *self)
+_ide_grid_column_init_actions (IdeGridColumn *self)
{
g_autoptr(GSimpleActionGroup) group = NULL;
- g_assert (IDE_IS_LAYOUT_GRID_COLUMN (self));
+ g_assert (IDE_IS_GRID_COLUMN (self));
group = g_simple_action_group_new ();
g_action_map_add_action_entries (G_ACTION_MAP (group),
@@ -76,6 +76,6 @@ _ide_layout_grid_column_init_actions (IdeLayoutGridColumn *self)
G_N_ELEMENTS (grid_column_actions),
self);
gtk_widget_insert_action_group (GTK_WIDGET (self),
- "layoutgridcolumn",
+ "gridcolumn",
G_ACTION_GROUP (group));
}
diff --git a/src/libide/layout/ide-layout-grid-column.c b/src/libide/gui/ide-grid-column.c
similarity index 53%
rename from src/libide/layout/ide-layout-grid-column.c
rename to src/libide/gui/ide-grid-column.c
index e1b8ad4a6..1fbee766a 100644
--- a/src/libide/layout/ide-layout-grid-column.c
+++ b/src/libide/gui/ide-grid-column.c
@@ -1,4 +1,4 @@
-/* ide-layout-grid-column.c
+/* ide-grid-column.c
*
* Copyright 2017-2019 Christian Hergert <chergert redhat com>
*
@@ -18,16 +18,18 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
-#define G_LOG_DOMAIN "ide-layout-grid-column"
+#define G_LOG_DOMAIN "ide-grid-column"
#include "config.h"
-#include "layout/ide-layout-grid-column.h"
-#include "layout/ide-layout-private.h"
-#include "layout/ide-layout-view.h"
-#include "threading/ide-task.h"
+#include <libide-core.h>
+#include <libide-threading.h>
-struct _IdeLayoutGridColumn
+#include "ide-grid-column.h"
+#include "ide-gui-private.h"
+#include "ide-page.h"
+
+struct _IdeGridColumn
{
DzlMultiPaned parent_instance;
GQueue focus_stack;
@@ -39,9 +41,9 @@ typedef struct
IdeTask *backpointer;
} TryCloseState;
-G_DEFINE_TYPE (IdeLayoutGridColumn, ide_layout_grid_column, DZL_TYPE_MULTI_PANED)
+G_DEFINE_TYPE (IdeGridColumn, ide_grid_column, DZL_TYPE_MULTI_PANED)
-static void ide_layout_grid_column_try_close_pump (IdeTask *task);
+static void ide_grid_column_try_close_pump (IdeTask *task);
enum {
PROP_0,
@@ -66,14 +68,14 @@ try_close_state_free (gpointer data)
}
static void
-ide_layout_grid_column_add (GtkContainer *container,
- GtkWidget *widget)
+ide_grid_column_add (GtkContainer *container,
+ GtkWidget *widget)
{
- IdeLayoutGridColumn *self = (IdeLayoutGridColumn *)container;
+ IdeGridColumn *self = (IdeGridColumn *)container;
- g_assert (IDE_IS_LAYOUT_GRID_COLUMN (self));
+ g_assert (IDE_IS_GRID_COLUMN (self));
- if (IDE_IS_LAYOUT_VIEW (widget))
+ if (IDE_IS_PAGE (widget))
{
GtkWidget *child;
@@ -82,84 +84,84 @@ ide_layout_grid_column_add (GtkContainer *container,
child = dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (container), 0);
gtk_container_add (GTK_CONTAINER (child), widget);
}
- else if (IDE_IS_LAYOUT_STACK (widget))
+ else if (IDE_IS_FRAME (widget))
{
GtkWidget *grid;
g_queue_push_head (&self->focus_stack, widget);
- GTK_CONTAINER_CLASS (ide_layout_grid_column_parent_class)->add (container, widget);
+ GTK_CONTAINER_CLASS (ide_grid_column_parent_class)->add (container, widget);
- if (IDE_IS_LAYOUT_GRID (grid = gtk_widget_get_parent (GTK_WIDGET (self))))
- _ide_layout_grid_stack_added (IDE_LAYOUT_GRID (grid), IDE_LAYOUT_STACK (widget));
+ if (IDE_IS_GRID (grid = gtk_widget_get_parent (GTK_WIDGET (self))))
+ _ide_grid_stack_added (IDE_GRID (grid), IDE_FRAME (widget));
}
else
{
- g_warning ("%s only supports adding IdeLayoutView or IdeLayoutStack",
+ g_warning ("%s only supports adding IdePage or IdeFrame",
G_OBJECT_TYPE_NAME (self));
return;
}
}
static void
-ide_layout_grid_column_remove (GtkContainer *container,
- GtkWidget *widget)
+ide_grid_column_remove (GtkContainer *container,
+ GtkWidget *widget)
{
- IdeLayoutGridColumn *self = (IdeLayoutGridColumn *)container;
+ IdeGridColumn *self = (IdeGridColumn *)container;
GtkWidget *grid;
- g_assert (IDE_IS_LAYOUT_GRID_COLUMN (self));
- g_assert (IDE_IS_LAYOUT_STACK (widget));
+ g_assert (IDE_IS_GRID_COLUMN (self));
+ g_assert (IDE_IS_FRAME (widget));
- if (IDE_IS_LAYOUT_GRID (grid = gtk_widget_get_parent (GTK_WIDGET (self))))
- _ide_layout_grid_stack_removed (IDE_LAYOUT_GRID (grid), IDE_LAYOUT_STACK (widget));
+ if (IDE_IS_GRID (grid = gtk_widget_get_parent (GTK_WIDGET (self))))
+ _ide_grid_stack_removed (IDE_GRID (grid), IDE_FRAME (widget));
g_queue_remove (&self->focus_stack, widget);
- GTK_CONTAINER_CLASS (ide_layout_grid_column_parent_class)->remove (container, widget);
+ GTK_CONTAINER_CLASS (ide_grid_column_parent_class)->remove (container, widget);
}
static void
-ide_layout_grid_column_grab_focus (GtkWidget *widget)
+ide_grid_column_grab_focus (GtkWidget *widget)
{
- IdeLayoutGridColumn *self = (IdeLayoutGridColumn *)widget;
- IdeLayoutStack *stack;
+ IdeGridColumn *self = (IdeGridColumn *)widget;
+ IdeFrame *stack;
- g_assert (IDE_IS_LAYOUT_GRID_COLUMN (self));
+ g_assert (IDE_IS_GRID_COLUMN (self));
- stack = ide_layout_grid_column_get_current_stack (self);
+ stack = ide_grid_column_get_current_stack (self);
if (stack != NULL)
gtk_widget_grab_focus (GTK_WIDGET (stack));
else
- GTK_WIDGET_CLASS (ide_layout_grid_column_parent_class)->grab_focus (widget);
+ GTK_WIDGET_CLASS (ide_grid_column_parent_class)->grab_focus (widget);
}
static void
-ide_layout_grid_column_finalize (GObject *object)
+ide_grid_column_finalize (GObject *object)
{
#ifndef G_DISABLE_ASSERT
- IdeLayoutGridColumn *self = (IdeLayoutGridColumn *)object;
+ IdeGridColumn *self = (IdeGridColumn *)object;
g_assert (self->focus_stack.head == NULL);
g_assert (self->focus_stack.tail == NULL);
g_assert (self->focus_stack.length == 0);
#endif
- G_OBJECT_CLASS (ide_layout_grid_column_parent_class)->finalize (object);
+ G_OBJECT_CLASS (ide_grid_column_parent_class)->finalize (object);
}
static void
-ide_layout_grid_column_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
+ide_grid_column_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
{
- IdeLayoutGridColumn *self = IDE_LAYOUT_GRID_COLUMN (object);
+ IdeGridColumn *self = IDE_GRID_COLUMN (object);
switch (prop_id)
{
case PROP_CURRENT_STACK:
- g_value_set_object (value, ide_layout_grid_column_get_current_stack (self));
+ g_value_set_object (value, ide_grid_column_get_current_stack (self));
break;
default:
@@ -168,17 +170,17 @@ ide_layout_grid_column_get_property (GObject *object,
}
static void
-ide_layout_grid_column_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
+ide_grid_column_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
{
- IdeLayoutGridColumn *self = IDE_LAYOUT_GRID_COLUMN (object);
+ IdeGridColumn *self = IDE_GRID_COLUMN (object);
switch (prop_id)
{
case PROP_CURRENT_STACK:
- ide_layout_grid_column_set_current_stack (self, g_value_get_object (value));
+ ide_grid_column_set_current_stack (self, g_value_get_object (value));
break;
default:
@@ -187,59 +189,59 @@ ide_layout_grid_column_set_property (GObject *object,
}
static void
-ide_layout_grid_column_class_init (IdeLayoutGridColumnClass *klass)
+ide_grid_column_class_init (IdeGridColumnClass *klass)
{
GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
- object_class->finalize = ide_layout_grid_column_finalize;
- object_class->get_property = ide_layout_grid_column_get_property;
- object_class->set_property = ide_layout_grid_column_set_property;
+ object_class->finalize = ide_grid_column_finalize;
+ object_class->get_property = ide_grid_column_get_property;
+ object_class->set_property = ide_grid_column_set_property;
- widget_class->grab_focus = ide_layout_grid_column_grab_focus;
+ widget_class->grab_focus = ide_grid_column_grab_focus;
- container_class->add = ide_layout_grid_column_add;
- container_class->remove = ide_layout_grid_column_remove;
+ container_class->add = ide_grid_column_add;
+ container_class->remove = ide_grid_column_remove;
properties [PROP_CURRENT_STACK] =
g_param_spec_object ("current-stack",
"Current Stack",
"The most recently focused stack within the column",
- IDE_TYPE_LAYOUT_STACK,
+ IDE_TYPE_FRAME,
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
- gtk_widget_class_set_css_name (widget_class, "idelayoutgridcolumn");
+ gtk_widget_class_set_css_name (widget_class, "idegridcolumn");
}
static void
-ide_layout_grid_column_init (IdeLayoutGridColumn *self)
+ide_grid_column_init (IdeGridColumn *self)
{
- _ide_layout_grid_column_init_actions (self);
+ _ide_grid_column_init_actions (self);
}
GtkWidget *
-ide_layout_grid_column_new (void)
+ide_grid_column_new (void)
{
- return g_object_new (IDE_TYPE_LAYOUT_GRID_COLUMN, NULL);
+ return g_object_new (IDE_TYPE_GRID_COLUMN, NULL);
}
static void
-ide_layout_grid_column_try_close_cb (GObject *object,
- GAsyncResult *result,
- gpointer user_data)
+ide_grid_column_try_close_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
{
- IdeLayoutStack *stack = (IdeLayoutStack *)object;
+ IdeFrame *stack = (IdeFrame *)object;
g_autoptr(IdeTask) task = user_data;
g_autoptr(GError) error = NULL;
- g_assert (IDE_IS_LAYOUT_STACK (stack));
+ g_assert (IDE_IS_FRAME (stack));
g_assert (G_IS_ASYNC_RESULT (result));
g_assert (IDE_IS_TASK (task));
- if (!ide_layout_stack_agree_to_close_finish (stack, result, &error))
+ if (!ide_frame_agree_to_close_finish (stack, result, &error))
{
g_debug ("Cannot close stack now due to: %s", error->message);
gtk_widget_grab_focus (GTK_WIDGET (stack));
@@ -249,14 +251,14 @@ ide_layout_grid_column_try_close_cb (GObject *object,
gtk_widget_destroy (GTK_WIDGET (stack));
- ide_layout_grid_column_try_close_pump (g_steal_pointer (&task));
+ ide_grid_column_try_close_pump (g_steal_pointer (&task));
}
static void
-ide_layout_grid_column_try_close_pump (IdeTask *_task)
+ide_grid_column_try_close_pump (IdeTask *_task)
{
g_autoptr(IdeTask) task = _task;
- g_autoptr(IdeLayoutStack) stack = NULL;
+ g_autoptr(IdeFrame) stack = NULL;
TryCloseState *state;
GCancellable *cancellable;
@@ -268,9 +270,9 @@ ide_layout_grid_column_try_close_pump (IdeTask *_task)
if (state->stacks == NULL)
{
- IdeLayoutGridColumn *self = ide_task_get_source_object (task);
+ IdeGridColumn *self = ide_task_get_source_object (task);
- g_assert (IDE_IS_LAYOUT_GRID_COLUMN (self));
+ g_assert (IDE_IS_GRID_COLUMN (self));
gtk_widget_destroy (GTK_WIDGET (self));
ide_task_return_boolean (task, TRUE);
return;
@@ -278,24 +280,24 @@ ide_layout_grid_column_try_close_pump (IdeTask *_task)
stack = state->stacks->data;
state->stacks = g_list_remove (state->stacks, stack);
- g_assert (IDE_IS_LAYOUT_STACK (stack));
+ g_assert (IDE_IS_FRAME (stack));
cancellable = ide_task_get_cancellable (task);
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
- ide_layout_stack_agree_to_close_async (stack,
+ ide_frame_agree_to_close_async (stack,
cancellable,
- ide_layout_grid_column_try_close_cb,
+ ide_grid_column_try_close_cb,
g_steal_pointer (&task));
}
void
-_ide_layout_grid_column_try_close (IdeLayoutGridColumn *self)
+_ide_grid_column_try_close (IdeGridColumn *self)
{
TryCloseState state = { 0 };
g_autoptr(IdeTask) task = NULL;
- g_return_if_fail (IDE_IS_LAYOUT_GRID_COLUMN (self));
+ g_return_if_fail (IDE_IS_GRID_COLUMN (self));
state.stacks = gtk_container_get_children (GTK_CONTAINER (self));
@@ -309,20 +311,20 @@ _ide_layout_grid_column_try_close (IdeLayoutGridColumn *self)
}
task = ide_task_new (self, NULL, NULL, NULL);
- ide_task_set_source_tag (task, _ide_layout_grid_column_try_close);
+ ide_task_set_source_tag (task, _ide_grid_column_try_close);
ide_task_set_priority (task, G_PRIORITY_LOW);
g_list_foreach (state.stacks, (GFunc)g_object_ref, NULL);
state.backpointer = task;
ide_task_set_task_data (task, g_slice_dup (TryCloseState, &state), try_close_state_free);
- ide_layout_grid_column_try_close_pump (g_steal_pointer (&task));
+ ide_grid_column_try_close_pump (g_steal_pointer (&task));
}
gboolean
-_ide_layout_grid_column_is_empty (IdeLayoutGridColumn *self)
+_ide_grid_column_is_empty (IdeGridColumn *self)
{
- g_return_val_if_fail (IDE_IS_LAYOUT_GRID_COLUMN (self), FALSE);
+ g_return_val_if_fail (IDE_IS_GRID_COLUMN (self), FALSE);
/*
* Check if we only have a single stack and it is empty.
@@ -333,41 +335,41 @@ _ide_layout_grid_column_is_empty (IdeLayoutGridColumn *self)
{
GtkWidget *child = dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (self), 0);
- g_assert (IDE_IS_LAYOUT_STACK (child));
+ g_assert (IDE_IS_FRAME (child));
- return !ide_layout_stack_get_has_view (IDE_LAYOUT_STACK (child));
+ return !ide_frame_get_has_page (IDE_FRAME (child));
}
return FALSE;
}
/**
- * ide_layout_grid_column_get_current_stack:
- * @self: a #IdeLayoutGridColumn
+ * ide_grid_column_get_current_stack:
+ * @self: a #IdeGridColumn
*
* Gets the most recently focused stack. If no stack has been added, then
* %NULL is returned.
*
- * Returns: (transfer none) (nullable): an #IdeLayoutStack or %NULL.
+ * Returns: (transfer none) (nullable): an #IdeFrame or %NULL.
*
* Since: 3.32
*/
-IdeLayoutStack *
-ide_layout_grid_column_get_current_stack (IdeLayoutGridColumn *self)
+IdeFrame *
+ide_grid_column_get_current_stack (IdeGridColumn *self)
{
- g_return_val_if_fail (IDE_IS_LAYOUT_GRID_COLUMN (self), NULL);
+ g_return_val_if_fail (IDE_IS_GRID_COLUMN (self), NULL);
return self->focus_stack.head ? self->focus_stack.head->data : NULL;
}
void
-ide_layout_grid_column_set_current_stack (IdeLayoutGridColumn *self,
- IdeLayoutStack *stack)
+ide_grid_column_set_current_stack (IdeGridColumn *self,
+ IdeFrame *stack)
{
GList *iter;
- g_return_if_fail (IDE_IS_LAYOUT_GRID_COLUMN (self));
- g_return_if_fail (!stack || IDE_IS_LAYOUT_STACK (stack));
+ g_return_if_fail (IDE_IS_GRID_COLUMN (self));
+ g_return_if_fail (!stack || IDE_IS_FRAME (stack));
/* If there is nothing to do, short-circuit. */
if (stack == NULL ||
diff --git a/src/libide/layout/ide-layout-grid-column.h b/src/libide/gui/ide-grid-column.h
similarity index 60%
rename from src/libide/layout/ide-layout-grid-column.h
rename to src/libide/gui/ide-grid-column.h
index 9708a4b08..ab2810fe0 100644
--- a/src/libide/layout/ide-layout-grid-column.h
+++ b/src/libide/gui/ide-grid-column.h
@@ -1,4 +1,4 @@
-/* ide-layout-grid-column.h
+/* ide-grid-column.h
*
* Copyright 2017-2019 Christian Hergert <chergert redhat com>
*
@@ -20,25 +20,28 @@
#pragma once
-#include <dazzle.h>
+#if !defined (IDE_GUI_INSIDE) && !defined (IDE_GUI_COMPILATION)
+# error "Only <libide-gui.h> can be included directly."
+#endif
-#include "ide-version-macros.h"
+#include <dazzle.h>
+#include <libide-core.h>
-#include "layout/ide-layout-stack.h"
+#include "ide-frame.h"
G_BEGIN_DECLS
-#define IDE_TYPE_LAYOUT_GRID_COLUMN (ide_layout_grid_column_get_type())
+#define IDE_TYPE_GRID_COLUMN (ide_grid_column_get_type())
IDE_AVAILABLE_IN_3_32
-G_DECLARE_FINAL_TYPE (IdeLayoutGridColumn, ide_layout_grid_column, IDE, LAYOUT_GRID_COLUMN, DzlMultiPaned)
+G_DECLARE_FINAL_TYPE (IdeGridColumn, ide_grid_column, IDE, GRID_COLUMN, DzlMultiPaned)
IDE_AVAILABLE_IN_3_32
-GtkWidget *ide_layout_grid_column_new (void);
+GtkWidget *ide_grid_column_new (void);
IDE_AVAILABLE_IN_3_32
-IdeLayoutStack *ide_layout_grid_column_get_current_stack (IdeLayoutGridColumn *self);
+IdeFrame *ide_grid_column_get_current_stack (IdeGridColumn *self);
IDE_AVAILABLE_IN_3_32
-void ide_layout_grid_column_set_current_stack (IdeLayoutGridColumn *self,
- IdeLayoutStack *stack);
+void ide_grid_column_set_current_stack (IdeGridColumn *self,
+ IdeFrame *stack);
G_END_DECLS
diff --git a/src/libide/layout/ide-layout-grid.c b/src/libide/gui/ide-grid.c
similarity index 56%
rename from src/libide/layout/ide-layout-grid.c
rename to src/libide/gui/ide-grid.c
index c5e319afd..47b8dc63a 100644
--- a/src/libide/layout/ide-layout-grid.c
+++ b/src/libide/gui/ide-grid.c
@@ -1,4 +1,4 @@
-/* ide-layout-grid.c
+/* ide-grid.c
*
* Copyright 2017-2019 Christian Hergert <chergert redhat com>
*
@@ -18,35 +18,34 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
-#define G_LOG_DOMAIN "ide-layout-grid"
+
+#define G_LOG_DOMAIN "ide-grid"
#include "config.h"
#include <string.h>
-#include "ide-object.h"
-
-#include "layout/ide-layout-grid.h"
-#include "layout/ide-layout-private.h"
+#include "ide-grid.h"
+#include "ide-gui-private.h"
/**
- * SECTION:ide-layout-grid
- * @title: IdeLayoutGrid
- * @short_description: A grid for #IdeLayoutView
+ * SECTION:ide-grid
+ * @title: IdeGrid
+ * @short_description: A grid for #IdePage
*
- * The #IdeLayoutGrid provides a grid of views that the user may
+ * The #IdeGrid provides a grid of pages that the user may
* manipulate.
*
- * Internally, this is implemented with #IdeLayoutGrid at the top
- * containing one or more of #IdeLayoutGridColumn. Those columns
- * contain one or more #IdeLayoutStack. The stack can contain many
- * #IdeLayoutView.
+ * Internally, this is implemented with #IdeGrid at the top
+ * containing one or more of #IdeGridColumn. Those columns
+ * contain one or more #IdeFrame. The stack can contain many
+ * #IdePage.
*
- * #IdeLayoutGrid implements the #GListModel interface to simplify
- * the process of listing (with deduplication) the views that are
- * contianed within the #IdeLayoutGrid. If you would instead like
- * to see all possible views in the stack, use the
- * ide_layout_grid_foreach_view() API.
+ * #IdeGrid implements the #GListModel interface to simplify
+ * the process of listing (with deduplication) the pages that are
+ * contianed within the #IdeGrid. If you would instead like
+ * to see all possible pages in the stack, use the
+ * ide_grid_foreach_page() API.
*
* Since: 3.32
*/
@@ -67,22 +66,22 @@ typedef struct
/*
* This unowned reference is simply used to compare to a new focus
- * view to see if we have changed our current view. It is not to
+ * page to see if we have changed our current page. It is not to
* be used directly, only for pointer comparison.
*/
- IdeLayoutView *_last_focused_view;
+ IdePage *_last_focused_page;
/*
* A GSource that is used to remove empty stacks that are unnecessary
* (after a last stack item is removed).
*/
guint cull_source;
-} IdeLayoutGridPrivate;
+} IdeGridPrivate;
typedef struct
{
- IdeLayoutGridColumn *column;
- IdeLayoutStack *stack;
+ IdeGridColumn *column;
+ IdeFrame *stack;
GdkRectangle area;
gint drop;
gint x;
@@ -91,7 +90,7 @@ typedef struct
typedef struct
{
- IdeLayoutStack *stack;
+ IdeFrame *stack;
guint len;
} StackInfo;
@@ -99,7 +98,7 @@ enum {
PROP_0,
PROP_CURRENT_COLUMN,
PROP_CURRENT_STACK,
- PROP_CURRENT_VIEW,
+ PROP_CURRENT_PAGE,
N_PROPS
};
@@ -119,28 +118,28 @@ enum {
static void list_model_iface_init (GListModelInterface *iface);
-G_DEFINE_TYPE_WITH_CODE (IdeLayoutGrid, ide_layout_grid, DZL_TYPE_MULTI_PANED,
- G_ADD_PRIVATE (IdeLayoutGrid)
+G_DEFINE_TYPE_WITH_CODE (IdeGrid, ide_grid, DZL_TYPE_MULTI_PANED,
+ G_ADD_PRIVATE (IdeGrid)
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init))
static GParamSpec *properties [N_PROPS];
static guint signals [N_SIGNALS];
static void
-ide_layout_grid_cull (IdeLayoutGrid *self)
+ide_grid_cull (IdeGrid *self)
{
guint n_columns;
- g_assert (IDE_IS_LAYOUT_GRID (self));
+ g_assert (IDE_IS_GRID (self));
n_columns = dzl_multi_paned_get_n_children (DZL_MULTI_PANED (self));
for (guint i = n_columns; i > 0; i--)
{
- IdeLayoutGridColumn *column;
+ IdeGridColumn *column;
guint n_stacks;
- column = IDE_LAYOUT_GRID_COLUMN (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (self), i - 1));
+ column = IDE_GRID_COLUMN (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (self), i - 1));
n_stacks = dzl_multi_paned_get_n_children (DZL_MULTI_PANED (column));
if (n_columns == 1 && n_stacks == 1)
@@ -148,10 +147,10 @@ ide_layout_grid_cull (IdeLayoutGrid *self)
for (guint j = n_stacks; j > 0; j--)
{
- IdeLayoutStack *stack;
+ IdeFrame *stack;
guint n_items;
- stack = IDE_LAYOUT_STACK (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (column), j - 1));
+ stack = IDE_FRAME (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (column), j - 1));
n_items = g_list_model_get_n_items (G_LIST_MODEL (stack));
if (n_items == 0)
@@ -164,42 +163,42 @@ ide_layout_grid_cull (IdeLayoutGrid *self)
}
static gboolean
-ide_layout_grid_do_cull (gpointer data)
+ide_grid_do_cull (gpointer data)
{
- IdeLayoutGrid *self = data;
- IdeLayoutGridPrivate *priv = ide_layout_grid_get_instance_private (self);
+ IdeGrid *self = data;
+ IdeGridPrivate *priv = ide_grid_get_instance_private (self);
- g_assert (IDE_IS_LAYOUT_GRID (self));
+ g_assert (IDE_IS_GRID (self));
priv->cull_source = 0;
- ide_layout_grid_cull (self);
+ ide_grid_cull (self);
return G_SOURCE_REMOVE;
}
static void
-ide_layout_grid_queue_cull (IdeLayoutGrid *self)
+ide_grid_queue_cull (IdeGrid *self)
{
- IdeLayoutGridPrivate *priv = ide_layout_grid_get_instance_private (self);
+ IdeGridPrivate *priv = ide_grid_get_instance_private (self);
- g_assert (IDE_IS_LAYOUT_GRID (self));
+ g_assert (IDE_IS_GRID (self));
if (priv->cull_source != 0)
return;
priv->cull_source = gdk_threads_add_idle_full (G_PRIORITY_HIGH,
- ide_layout_grid_do_cull,
+ ide_grid_do_cull,
g_object_ref (self),
g_object_unref);
}
static void
-ide_layout_grid_update_actions (IdeLayoutGrid *self)
+ide_grid_update_actions (IdeGrid *self)
{
guint n_children;
- g_assert (IDE_IS_LAYOUT_GRID (self));
+ g_assert (IDE_IS_GRID (self));
n_children = dzl_multi_paned_get_n_children (DZL_MULTI_PANED (self));
@@ -207,45 +206,45 @@ ide_layout_grid_update_actions (IdeLayoutGrid *self)
{
GtkWidget *column = dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (self), i);
- g_assert (IDE_IS_LAYOUT_GRID_COLUMN (column));
+ g_assert (IDE_IS_GRID_COLUMN (column));
- _ide_layout_grid_column_update_actions (IDE_LAYOUT_GRID_COLUMN (column));
+ _ide_grid_column_update_actions (IDE_GRID_COLUMN (column));
}
}
-static IdeLayoutStack *
-ide_layout_grid_real_create_stack (IdeLayoutGrid *self)
+static IdeFrame *
+ide_grid_real_create_frame (IdeGrid *self)
{
- return g_object_new (IDE_TYPE_LAYOUT_STACK,
+ return g_object_new (IDE_TYPE_FRAME,
"expand", TRUE,
"visible", TRUE,
NULL);
}
static GtkWidget *
-ide_layout_grid_create_stack (IdeLayoutGrid *self)
+ide_grid_create_frame (IdeGrid *self)
{
- IdeLayoutStack *ret = NULL;
+ IdeFrame *ret = NULL;
- g_assert (IDE_IS_LAYOUT_GRID (self));
+ g_assert (IDE_IS_GRID (self));
g_signal_emit (self, signals [CREATE_STACK], 0, &ret);
- g_return_val_if_fail (IDE_IS_LAYOUT_STACK (ret), NULL);
+ g_return_val_if_fail (IDE_IS_FRAME (ret), NULL);
return GTK_WIDGET (ret);
}
static GtkWidget *
-ide_layout_grid_create_column (IdeLayoutGrid *self)
+ide_grid_create_column (IdeGrid *self)
{
GtkWidget *stack;
- g_assert (IDE_IS_LAYOUT_GRID (self));
+ g_assert (IDE_IS_GRID (self));
- stack = ide_layout_grid_create_stack (self);
+ stack = ide_grid_create_frame (self);
if (stack != NULL)
{
- GtkWidget *column = g_object_new (IDE_TYPE_LAYOUT_GRID_COLUMN,
+ GtkWidget *column = g_object_new (IDE_TYPE_GRID_COLUMN,
"visible", TRUE,
NULL);
gtk_container_add (GTK_CONTAINER (column), stack);
@@ -256,61 +255,61 @@ ide_layout_grid_create_column (IdeLayoutGrid *self)
}
static void
-ide_layout_grid_after_set_focus (IdeLayoutGrid *self,
+ide_grid_after_set_focus (IdeGrid *self,
GtkWidget *widget,
GtkWidget *toplevel)
{
- IdeLayoutGridPrivate *priv = ide_layout_grid_get_instance_private (self);
+ IdeGridPrivate *priv = ide_grid_get_instance_private (self);
- g_assert (IDE_IS_LAYOUT_GRID (self));
+ g_assert (IDE_IS_GRID (self));
g_assert (!widget || GTK_IS_WIDGET (widget));
g_assert (GTK_IS_WINDOW (toplevel));
if (widget != NULL)
{
GtkWidget *column = NULL;
- GtkWidget *view;
+ GtkWidget *page;
if (gtk_widget_is_ancestor (widget, GTK_WIDGET (self)))
{
- column = gtk_widget_get_ancestor (widget, IDE_TYPE_LAYOUT_GRID_COLUMN);
+ column = gtk_widget_get_ancestor (widget, IDE_TYPE_GRID_COLUMN);
if (column != NULL)
- ide_layout_grid_set_current_column (self, IDE_LAYOUT_GRID_COLUMN (column));
+ ide_grid_set_current_column (self, IDE_GRID_COLUMN (column));
}
/*
- * self->_last_focused_view is an unowned reference, we only
+ * self->_last_focused_page is an unowned reference, we only
* use it for pointer comparison, nothing more.
*/
- view = gtk_widget_get_ancestor (widget, IDE_TYPE_LAYOUT_VIEW);
- if (view != (GtkWidget *)priv->_last_focused_view)
+ page = gtk_widget_get_ancestor (widget, IDE_TYPE_PAGE);
+ if (page != (GtkWidget *)priv->_last_focused_page)
{
- priv->_last_focused_view = (IdeLayoutView *)view;
- ide_object_notify_in_main (self, properties [PROP_CURRENT_VIEW]);
+ priv->_last_focused_page = (IdePage *)page;
+ ide_object_notify_in_main (self, properties [PROP_CURRENT_PAGE]);
- if (view != NULL && column != NULL)
+ if (page != NULL && column != NULL)
{
GtkWidget *stack;
- stack = gtk_widget_get_ancestor (GTK_WIDGET (view), IDE_TYPE_LAYOUT_STACK);
+ stack = gtk_widget_get_ancestor (GTK_WIDGET (page), IDE_TYPE_FRAME);
if (stack != NULL)
- ide_layout_grid_column_set_current_stack (IDE_LAYOUT_GRID_COLUMN (column),
- IDE_LAYOUT_STACK (stack));
+ ide_grid_column_set_current_stack (IDE_GRID_COLUMN (column),
+ IDE_FRAME (stack));
}
}
}
}
static void
-ide_layout_grid_hierarchy_changed (GtkWidget *widget,
+ide_grid_hierarchy_changed (GtkWidget *widget,
GtkWidget *old_toplevel)
{
- IdeLayoutGrid *self = (IdeLayoutGrid *)widget;
- IdeLayoutGridPrivate *priv = ide_layout_grid_get_instance_private (self);
+ IdeGrid *self = (IdeGrid *)widget;
+ IdeGridPrivate *priv = ide_grid_get_instance_private (self);
GtkWidget *toplevel;
- g_assert (IDE_IS_LAYOUT_GRID (self));
+ g_assert (IDE_IS_GRID (self));
g_assert (!old_toplevel || GTK_IS_WIDGET (old_toplevel));
/*
@@ -334,54 +333,54 @@ ide_layout_grid_hierarchy_changed (GtkWidget *widget,
if (dzl_multi_paned_get_n_children (DZL_MULTI_PANED (widget)) == 0)
{
- GtkWidget *column = ide_layout_grid_create_column (self);
+ GtkWidget *column = ide_grid_create_column (self);
gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (column));
}
}
static void
-ide_layout_grid_add (GtkContainer *container,
+ide_grid_add (GtkContainer *container,
GtkWidget *widget)
{
- IdeLayoutGrid *self = (IdeLayoutGrid *)container;
- IdeLayoutGridPrivate *priv = ide_layout_grid_get_instance_private (self);
+ IdeGrid *self = (IdeGrid *)container;
+ IdeGridPrivate *priv = ide_grid_get_instance_private (self);
- g_assert (IDE_IS_LAYOUT_GRID (self));
+ g_assert (IDE_IS_GRID (self));
g_assert (GTK_IS_WIDGET (widget));
- if (IDE_IS_LAYOUT_GRID_COLUMN (widget))
+ if (IDE_IS_GRID_COLUMN (widget))
{
GList *children;
/* Add our column to the grid */
g_queue_push_head (&priv->focus_column, widget);
- GTK_CONTAINER_CLASS (ide_layout_grid_parent_class)->add (container, widget);
- ide_layout_grid_set_current_column (self, IDE_LAYOUT_GRID_COLUMN (widget));
- _ide_layout_grid_column_update_actions (IDE_LAYOUT_GRID_COLUMN (widget));
+ GTK_CONTAINER_CLASS (ide_grid_parent_class)->add (container, widget);
+ ide_grid_set_current_column (self, IDE_GRID_COLUMN (widget));
+ _ide_grid_column_update_actions (IDE_GRID_COLUMN (widget));
- /* Start monitoring all the stacks in the grid for views */
+ /* Start monitoring all the stacks in the grid for pages */
children = gtk_container_get_children (GTK_CONTAINER (widget));
for (const GList *iter = children; iter; iter = iter->next)
- if (IDE_IS_LAYOUT_STACK (iter->data))
- _ide_layout_grid_stack_added (self, iter->data);
+ if (IDE_IS_FRAME (iter->data))
+ _ide_grid_stack_added (self, iter->data);
g_list_free (children);
}
- else if (IDE_IS_LAYOUT_STACK (widget))
+ else if (IDE_IS_FRAME (widget))
{
- IdeLayoutGridColumn *column;
+ IdeGridColumn *column;
- column = ide_layout_grid_get_current_column (self);
+ column = ide_grid_get_current_column (self);
gtk_container_add (GTK_CONTAINER (column), widget);
- ide_layout_grid_set_current_column (self, column);
+ ide_grid_set_current_column (self, column);
}
- else if (IDE_IS_LAYOUT_VIEW (widget))
+ else if (IDE_IS_PAGE (widget))
{
- IdeLayoutGridColumn *column = NULL;
+ IdeGridColumn *column = NULL;
guint n_columns;
/* If we have an empty layout stack, we'll prefer to add the
- * view to that. If we don't find an empty stack, we'll add
- * the view to the most recently focused stack.
+ * page to that. If we don't find an empty stack, we'll add
+ * the page to the most recently focused stack.
*/
n_columns = dzl_multi_paned_get_n_children (DZL_MULTI_PANED (self));
@@ -390,49 +389,49 @@ ide_layout_grid_add (GtkContainer *container,
{
GtkWidget *ele = dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (self), i);
- g_assert (IDE_IS_LAYOUT_GRID_COLUMN (ele));
+ g_assert (IDE_IS_GRID_COLUMN (ele));
- if (_ide_layout_grid_column_is_empty (IDE_LAYOUT_GRID_COLUMN (ele)))
+ if (_ide_grid_column_is_empty (IDE_GRID_COLUMN (ele)))
{
- column = IDE_LAYOUT_GRID_COLUMN (ele);
+ column = IDE_GRID_COLUMN (ele);
break;
}
}
if (column == NULL)
- column = ide_layout_grid_get_current_column (self);
+ column = ide_grid_get_current_column (self);
- g_assert (IDE_IS_LAYOUT_GRID_COLUMN (column));
+ g_assert (IDE_IS_GRID_COLUMN (column));
gtk_container_add (GTK_CONTAINER (column), widget);
}
else
{
- g_warning ("%s must be one of IdeLayoutStack, IdeLayoutView, or IdeLayoutGrid",
+ g_warning ("%s must be one of IdeFrame, IdePage, or IdeGrid",
G_OBJECT_TYPE_NAME (self));
return;
}
- ide_layout_grid_update_actions (self);
+ ide_grid_update_actions (self);
}
static void
-ide_layout_grid_remove (GtkContainer *container,
+ide_grid_remove (GtkContainer *container,
GtkWidget *widget)
{
- IdeLayoutGrid *self = (IdeLayoutGrid *)container;
- IdeLayoutGridPrivate *priv = ide_layout_grid_get_instance_private (self);
+ IdeGrid *self = (IdeGrid *)container;
+ IdeGridPrivate *priv = ide_grid_get_instance_private (self);
gboolean notify = FALSE;
- g_assert (IDE_IS_LAYOUT_GRID (self));
- g_assert (IDE_IS_LAYOUT_GRID_COLUMN (widget));
+ g_assert (IDE_IS_GRID (self));
+ g_assert (IDE_IS_GRID_COLUMN (widget));
notify = g_queue_peek_head (&priv->focus_column) == (gpointer)widget;
g_queue_remove (&priv->focus_column, widget);
- GTK_CONTAINER_CLASS (ide_layout_grid_parent_class)->remove (container, widget);
+ GTK_CONTAINER_CLASS (ide_grid_parent_class)->remove (container, widget);
- ide_layout_grid_update_actions (self);
+ ide_grid_update_actions (self);
if (notify)
{
@@ -446,19 +445,19 @@ ide_layout_grid_remove (GtkContainer *container,
}
static gboolean
-ide_layout_grid_get_drop_area (IdeLayoutGrid *self,
+ide_grid_get_drop_area (IdeGrid *self,
gint x,
gint y,
GdkRectangle *out_area,
- IdeLayoutGridColumn **out_column,
- IdeLayoutStack **out_stack,
+ IdeGridColumn **out_column,
+ IdeFrame **out_stack,
gint *out_drop)
{
GtkAllocation alloc;
GtkWidget *column;
GtkWidget *stack = NULL;
- g_assert (IDE_IS_LAYOUT_GRID (self));
+ g_assert (IDE_IS_GRID (self));
g_assert (out_area != NULL);
g_assert (out_column != NULL);
g_assert (out_stack != NULL);
@@ -482,8 +481,8 @@ ide_layout_grid_get_drop_area (IdeLayoutGrid *self,
&stack_alloc.x, &stack_alloc.y);
*out_area = stack_alloc;
- *out_column = IDE_LAYOUT_GRID_COLUMN (column);
- *out_stack = IDE_LAYOUT_STACK (stack);
+ *out_column = IDE_GRID_COLUMN (column);
+ *out_stack = IDE_FRAME (stack);
*out_drop = DROP_ONTO;
gtk_widget_translate_coordinates (GTK_WIDGET (self), stack, x, y, &x, &y);
@@ -523,22 +522,22 @@ ide_layout_grid_get_drop_area (IdeLayoutGrid *self,
}
static gboolean
-ide_layout_grid_drag_motion (GtkWidget *widget,
+ide_grid_drag_motion (GtkWidget *widget,
GdkDragContext *context,
gint x,
gint y,
guint time_)
{
- IdeLayoutGrid *self = (IdeLayoutGrid *)widget;
- IdeLayoutGridPrivate *priv = ide_layout_grid_get_instance_private (self);
- IdeLayoutGridColumn *column = NULL;
- IdeLayoutStack *stack = NULL;
+ IdeGrid *self = (IdeGrid *)widget;
+ IdeGridPrivate *priv = ide_grid_get_instance_private (self);
+ IdeGridColumn *column = NULL;
+ IdeFrame *stack = NULL;
DzlAnimation *drag_anim;
GdkRectangle area = {0};
GtkAllocation alloc;
gint drop = DROP_ONTO;
- g_assert (IDE_IS_LAYOUT_GRID (self));
+ g_assert (IDE_IS_GRID (self));
g_assert (GDK_IS_DRAG_CONTEXT (context));
if (priv->drag_anim != NULL)
@@ -549,7 +548,7 @@ ide_layout_grid_drag_motion (GtkWidget *widget,
gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
- if (!ide_layout_grid_get_drop_area (self, x, y, &area, &column, &stack, &drop))
+ if (!ide_grid_get_drop_area (self, x, y, &area, &column, &stack, &drop))
return GDK_EVENT_PROPAGATE;
if (priv->drag_theatric == NULL)
@@ -583,7 +582,7 @@ ide_layout_grid_drag_motion (GtkWidget *widget,
}
static void
-ide_layout_grid_drag_data_received (GtkWidget *widget,
+ide_grid_drag_data_received (GtkWidget *widget,
GdkDragContext *context,
gint x,
gint y,
@@ -591,21 +590,21 @@ ide_layout_grid_drag_data_received (GtkWidget *widget,
guint info,
guint time_)
{
- IdeLayoutGrid *self = (IdeLayoutGrid *)widget;
- IdeLayoutGridColumn *column = NULL;
- IdeLayoutStack *stack = NULL;
+ IdeGrid *self = (IdeGrid *)widget;
+ IdeGridColumn *column = NULL;
+ IdeFrame *stack = NULL;
g_auto(GStrv) uris = NULL;
GdkRectangle area = {0};
gint drop = DROP_ONTO;
- g_assert (IDE_IS_LAYOUT_GRID (self));
+ g_assert (IDE_IS_GRID (self));
g_assert (GDK_IS_DRAG_CONTEXT (context));
- if (!ide_layout_grid_get_drop_area (self, x, y, &area, &column, &stack, &drop))
+ if (!ide_grid_get_drop_area (self, x, y, &area, &column, &stack, &drop))
return;
- g_assert (IDE_IS_LAYOUT_GRID_COLUMN (column));
- g_assert (IDE_IS_LAYOUT_STACK (stack));
+ g_assert (IDE_IS_GRID_COLUMN (column));
+ g_assert (IDE_IS_FRAME (stack));
if (!(uris = gtk_selection_data_get_uris (data)))
return;
@@ -613,15 +612,15 @@ ide_layout_grid_drag_data_received (GtkWidget *widget,
for (guint i = 0; uris[i] != NULL; i++)
{
const gchar *uri = uris[i];
- IdeLayoutView *view = NULL;
+ IdePage *page = NULL;
gint column_index = 0;
gint stack_index = 0;
- g_signal_emit (self, signals [CREATE_VIEW], 0, uri, &view);
+ g_signal_emit (self, signals [CREATE_VIEW], 0, uri, &page);
- if (view == NULL)
+ if (page == NULL)
{
- g_debug ("Failed to load IdeLayoutView for \"%s\"", uri);
+ g_debug ("Failed to load IdePage for \"%s\"", uri);
continue;
}
@@ -635,39 +634,39 @@ ide_layout_grid_drag_data_received (GtkWidget *widget,
switch (drop)
{
case DROP_ONTO:
- gtk_container_add (GTK_CONTAINER (stack), GTK_WIDGET (view));
+ gtk_container_add (GTK_CONTAINER (stack), GTK_WIDGET (page));
break;
case DROP_ABOVE:
- stack = IDE_LAYOUT_STACK (ide_layout_grid_create_stack (self));
+ stack = IDE_FRAME (ide_grid_create_frame (self));
gtk_container_add_with_properties (GTK_CONTAINER (column), GTK_WIDGET (stack),
"index", stack_index,
NULL);
- gtk_container_add (GTK_CONTAINER (stack), GTK_WIDGET (view));
+ gtk_container_add (GTK_CONTAINER (stack), GTK_WIDGET (page));
break;
case DROP_BELOW:
- stack = IDE_LAYOUT_STACK (ide_layout_grid_create_stack (self));
+ stack = IDE_FRAME (ide_grid_create_frame (self));
gtk_container_add_with_properties (GTK_CONTAINER (column), GTK_WIDGET (stack),
"index", stack_index + 1,
NULL);
- gtk_container_add (GTK_CONTAINER (stack), GTK_WIDGET (view));
+ gtk_container_add (GTK_CONTAINER (stack), GTK_WIDGET (page));
break;
case DROP_LEFT_OF:
- column = IDE_LAYOUT_GRID_COLUMN (ide_layout_grid_create_column (self));
+ column = IDE_GRID_COLUMN (ide_grid_create_column (self));
gtk_container_add_with_properties (GTK_CONTAINER (self), GTK_WIDGET (column),
"index", column_index,
NULL);
- gtk_container_add (GTK_CONTAINER (column), GTK_WIDGET (view));
+ gtk_container_add (GTK_CONTAINER (column), GTK_WIDGET (page));
break;
case DROP_RIGHT_OF:
- column = IDE_LAYOUT_GRID_COLUMN (ide_layout_grid_create_column (self));
+ column = IDE_GRID_COLUMN (ide_grid_create_column (self));
gtk_container_add_with_properties (GTK_CONTAINER (self), GTK_WIDGET (column),
"index", column_index + 1,
NULL);
- gtk_container_add (GTK_CONTAINER (column), GTK_WIDGET (view));
+ gtk_container_add (GTK_CONTAINER (column), GTK_WIDGET (page));
break;
default:
@@ -677,14 +676,14 @@ ide_layout_grid_drag_data_received (GtkWidget *widget,
}
static void
-ide_layout_grid_drag_leave (GtkWidget *widget,
+ide_grid_drag_leave (GtkWidget *widget,
GdkDragContext *context,
guint time_)
{
- IdeLayoutGrid *self = (IdeLayoutGrid *)widget;
- IdeLayoutGridPrivate *priv = ide_layout_grid_get_instance_private (self);
+ IdeGrid *self = (IdeGrid *)widget;
+ IdeGridPrivate *priv = ide_grid_get_instance_private (self);
- g_assert (IDE_IS_LAYOUT_GRID (self));
+ g_assert (IDE_IS_GRID (self));
g_assert (GDK_IS_DRAG_CONTEXT (context));
if (priv->drag_anim != NULL)
@@ -698,14 +697,14 @@ ide_layout_grid_drag_leave (GtkWidget *widget,
}
static gboolean
-ide_layout_grid_drag_failed (GtkWidget *widget,
+ide_grid_drag_failed (GtkWidget *widget,
GdkDragContext *context,
GtkDragResult result)
{
- IdeLayoutGrid *self = (IdeLayoutGrid *)widget;
- IdeLayoutGridPrivate *priv = ide_layout_grid_get_instance_private (self);
+ IdeGrid *self = (IdeGrid *)widget;
+ IdeGridPrivate *priv = ide_grid_get_instance_private (self);
- g_assert (IDE_IS_LAYOUT_GRID (self));
+ g_assert (IDE_IS_GRID (self));
g_assert (GDK_IS_DRAG_CONTEXT (context));
if (priv->drag_anim != NULL)
@@ -721,39 +720,39 @@ ide_layout_grid_drag_failed (GtkWidget *widget,
}
static void
-ide_layout_grid_grab_focus (GtkWidget *widget)
+ide_grid_grab_focus (GtkWidget *widget)
{
- IdeLayoutGrid *self = (IdeLayoutGrid *)widget;
- IdeLayoutStack *stack;
+ IdeGrid *self = (IdeGrid *)widget;
+ IdeFrame *stack;
- g_assert (IDE_IS_LAYOUT_GRID (self));
+ g_assert (IDE_IS_GRID (self));
- stack = ide_layout_grid_get_current_stack (self);
+ stack = ide_grid_get_current_stack (self);
if (stack != NULL)
gtk_widget_grab_focus (GTK_WIDGET (stack));
else
- GTK_WIDGET_CLASS (ide_layout_grid_parent_class)->grab_focus (widget);
+ GTK_WIDGET_CLASS (ide_grid_parent_class)->grab_focus (widget);
}
static void
-ide_layout_grid_destroy (GtkWidget *widget)
+ide_grid_destroy (GtkWidget *widget)
{
- IdeLayoutGrid *self = (IdeLayoutGrid *)widget;
- IdeLayoutGridPrivate *priv = ide_layout_grid_get_instance_private (self);
+ IdeGrid *self = (IdeGrid *)widget;
+ IdeGridPrivate *priv = ide_grid_get_instance_private (self);
dzl_clear_source (&priv->cull_source);
- GTK_WIDGET_CLASS (ide_layout_grid_parent_class)->destroy (widget);
+ GTK_WIDGET_CLASS (ide_grid_parent_class)->destroy (widget);
}
static void
-ide_layout_grid_finalize (GObject *object)
+ide_grid_finalize (GObject *object)
{
- IdeLayoutGrid *self = (IdeLayoutGrid *)object;
- IdeLayoutGridPrivate *priv = ide_layout_grid_get_instance_private (self);
+ IdeGrid *self = (IdeGrid *)object;
+ IdeGridPrivate *priv = ide_grid_get_instance_private (self);
- g_assert (IDE_IS_LAYOUT_GRID (self));
+ g_assert (IDE_IS_GRID (self));
g_assert (priv->focus_column.head == NULL);
g_assert (priv->focus_column.tail == NULL);
g_assert (priv->focus_column.length == 0);
@@ -761,25 +760,25 @@ ide_layout_grid_finalize (GObject *object)
g_clear_pointer (&priv->stack_info, g_array_unref);
g_clear_object (&priv->toplevel_signals);
- G_OBJECT_CLASS (ide_layout_grid_parent_class)->finalize (object);
+ G_OBJECT_CLASS (ide_grid_parent_class)->finalize (object);
}
static void
-ide_layout_grid_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
+ide_grid_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
{
- IdeLayoutGrid *self = IDE_LAYOUT_GRID (object);
+ IdeGrid *self = IDE_GRID (object);
switch (prop_id)
{
case PROP_CURRENT_COLUMN:
- g_value_set_object (value, ide_layout_grid_get_current_column (self));
+ g_value_set_object (value, ide_grid_get_current_column (self));
break;
case PROP_CURRENT_STACK:
- g_value_set_object (value, ide_layout_grid_get_current_stack (self));
+ g_value_set_object (value, ide_grid_get_current_stack (self));
break;
default:
@@ -788,17 +787,17 @@ ide_layout_grid_get_property (GObject *object,
}
static void
-ide_layout_grid_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
+ide_grid_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
{
- IdeLayoutGrid *self = IDE_LAYOUT_GRID (object);
+ IdeGrid *self = IDE_GRID (object);
switch (prop_id)
{
case PROP_CURRENT_COLUMN:
- ide_layout_grid_set_current_column (self, g_value_get_object (value));
+ ide_grid_set_current_column (self, g_value_get_object (value));
break;
default:
@@ -807,61 +806,61 @@ ide_layout_grid_set_property (GObject *object,
}
static void
-ide_layout_grid_class_init (IdeLayoutGridClass *klass)
+ide_grid_class_init (IdeGridClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
- object_class->finalize = ide_layout_grid_finalize;
- object_class->get_property = ide_layout_grid_get_property;
- object_class->set_property = ide_layout_grid_set_property;
+ object_class->finalize = ide_grid_finalize;
+ object_class->get_property = ide_grid_get_property;
+ object_class->set_property = ide_grid_set_property;
- widget_class->destroy = ide_layout_grid_destroy;
- widget_class->drag_data_received = ide_layout_grid_drag_data_received;
- widget_class->drag_motion = ide_layout_grid_drag_motion;
- widget_class->drag_leave = ide_layout_grid_drag_leave;
- widget_class->drag_failed = ide_layout_grid_drag_failed;
- widget_class->grab_focus = ide_layout_grid_grab_focus;
- widget_class->hierarchy_changed = ide_layout_grid_hierarchy_changed;
+ widget_class->destroy = ide_grid_destroy;
+ widget_class->drag_data_received = ide_grid_drag_data_received;
+ widget_class->drag_motion = ide_grid_drag_motion;
+ widget_class->drag_leave = ide_grid_drag_leave;
+ widget_class->drag_failed = ide_grid_drag_failed;
+ widget_class->grab_focus = ide_grid_grab_focus;
+ widget_class->hierarchy_changed = ide_grid_hierarchy_changed;
- container_class->add = ide_layout_grid_add;
- container_class->remove = ide_layout_grid_remove;
+ container_class->add = ide_grid_add;
+ container_class->remove = ide_grid_remove;
- klass->create_stack = ide_layout_grid_real_create_stack;
+ klass->create_frame = ide_grid_real_create_frame;
properties [PROP_CURRENT_COLUMN] =
g_param_spec_object ("current-column",
"Current Column",
"The most recently focused grid column",
- IDE_TYPE_LAYOUT_GRID_COLUMN,
+ IDE_TYPE_GRID_COLUMN,
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
properties [PROP_CURRENT_STACK] =
g_param_spec_object ("current-stack",
"Current Stack",
- "The most recently focused IdeLayoutStack",
- IDE_TYPE_LAYOUT_STACK,
+ "The most recently focused IdeFrame",
+ IDE_TYPE_FRAME,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
- properties [PROP_CURRENT_VIEW] =
- g_param_spec_object ("current-view",
+ properties [PROP_CURRENT_PAGE] =
+ g_param_spec_object ("current-page",
"Current View",
- "The most recently focused IdeLayoutView",
- IDE_TYPE_LAYOUT_VIEW,
+ "The most recently focused IdePage",
+ IDE_TYPE_PAGE,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
- gtk_widget_class_set_css_name (widget_class, "idelayoutgrid");
+ gtk_widget_class_set_css_name (widget_class, "idegrid");
/**
- * IdeLayoutGrid::create-stack:
- * @self: an #IdeLayoutGrid
+ * IdeGrid::create-stack:
+ * @self: an #IdeGrid
*
* Creates a new stack to be added to the grid.
*
- * Returns: (transfer full): A newly created #IdeLayoutStack
+ * Returns: (transfer full): A newly created #IdeFrame
*
* Since: 3.32
*/
@@ -869,40 +868,43 @@ ide_layout_grid_class_init (IdeLayoutGridClass *klass)
g_signal_new (g_intern_static_string ("create-stack"),
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (IdeLayoutGridClass, create_stack),
+ G_STRUCT_OFFSET (IdeGridClass, create_frame),
g_signal_accumulator_first_wins, NULL, NULL,
- IDE_TYPE_LAYOUT_STACK, 0);
+ IDE_TYPE_FRAME, 0);
/**
- * IdeLayoutGrid::create-view:
- * @self: an #IdeLayoutGrid
+ * IdeGrid::create-page:
+ * @self: an #IdeGrid
* @uri: the URI to open
*
- * Creates a new view for @uri to be added to the grid.
+ * Creates a new page for @uri to be added to the grid.
*
- * Returns: (transfer full): A newly created #IdeLayoutView
+ * Returns: (transfer full): A newly created #IdePage
*
* Since: 3.32
*/
signals [CREATE_VIEW] =
- g_signal_new (g_intern_static_string ("create-view"),
+ g_signal_new (g_intern_static_string ("create-page"),
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (IdeLayoutGridClass, create_view),
+ G_STRUCT_OFFSET (IdeGridClass, create_page),
g_signal_accumulator_first_wins, NULL, NULL,
- IDE_TYPE_LAYOUT_VIEW,
+ IDE_TYPE_PAGE,
1,
G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE);
}
static void
-ide_layout_grid_init (IdeLayoutGrid *self)
+ide_grid_init (IdeGrid *self)
{
- IdeLayoutGridPrivate *priv = ide_layout_grid_get_instance_private (self);
+ IdeGridPrivate *priv = ide_grid_get_instance_private (self);
static const GtkTargetEntry target_entries[] = {
{ (gchar *)"text/uri-list", 0, 0 },
};
+ gtk_orientable_set_orientation (GTK_ORIENTABLE (self),
+ GTK_ORIENTATION_HORIZONTAL);
+
gtk_drag_dest_set (GTK_WIDGET (self),
GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
target_entries,
@@ -915,56 +917,56 @@ ide_layout_grid_init (IdeLayoutGrid *self)
dzl_signal_group_connect_object (priv->toplevel_signals,
"set-focus",
- G_CALLBACK (ide_layout_grid_after_set_focus),
+ G_CALLBACK (ide_grid_after_set_focus),
self,
G_CONNECT_SWAPPED | G_CONNECT_AFTER);
- _ide_layout_grid_init_actions (self);
+ _ide_grid_init_actions (self);
}
/**
- * ide_layout_grid_new:
+ * ide_grid_new:
*
- * Creates a new #IdeLayoutGrid.
+ * Creates a new #IdeGrid.
*
- * Returns: (transfer full): A newly created #IdeLayoutGrid
+ * Returns: (transfer full): A newly created #IdeGrid
*
* Since: 3.32
*/
GtkWidget *
-ide_layout_grid_new (void)
+ide_grid_new (void)
{
- return g_object_new (IDE_TYPE_LAYOUT_GRID, NULL);
+ return g_object_new (IDE_TYPE_GRID, NULL);
}
/**
- * ide_layout_grid_get_current_stack:
- * @self: a #IdeLayoutGrid
+ * ide_grid_get_current_stack:
+ * @self: a #IdeGrid
*
* Gets the most recently focused stack. This is useful when you want to open
* a document on the stack the user last focused.
*
- * Returns: (transfer none) (nullable): an #IdeLayoutStack or %NULL.
+ * Returns: (transfer none) (nullable): an #IdeFrame or %NULL.
*
* Since: 3.32
*/
-IdeLayoutStack *
-ide_layout_grid_get_current_stack (IdeLayoutGrid *self)
+IdeFrame *
+ide_grid_get_current_stack (IdeGrid *self)
{
- IdeLayoutGridColumn *column;
+ IdeGridColumn *column;
- g_return_val_if_fail (IDE_IS_LAYOUT_GRID (self), NULL);
+ g_return_val_if_fail (IDE_IS_GRID (self), NULL);
- column = ide_layout_grid_get_current_column (self);
+ column = ide_grid_get_current_column (self);
if (column != NULL)
- return ide_layout_grid_column_get_current_stack (column);
+ return ide_grid_column_get_current_stack (column);
return NULL;
}
/**
- * ide_layout_grid_get_nth_column:
- * @self: a #IdeLayoutGrid
+ * ide_grid_get_nth_column:
+ * @self: a #IdeGrid
* @nth: the index of the column, or -1
*
* Gets the @nth column from the grid.
@@ -975,28 +977,28 @@ ide_layout_grid_get_current_stack (IdeLayoutGrid *self)
* If @nth is >= the number of columns in the grid, then a new
* column at the end of the grid is created.
*
- * Returns: (transfer none): An #IdeLayoutGridColumn.
+ * Returns: (transfer none): An #IdeGridColumn.
*
* Since: 3.32
*/
-IdeLayoutGridColumn *
-ide_layout_grid_get_nth_column (IdeLayoutGrid *self,
+IdeGridColumn *
+ide_grid_get_nth_column (IdeGrid *self,
gint nth)
{
GtkWidget *column;
- g_return_val_if_fail (IDE_IS_LAYOUT_GRID (self), NULL);
+ g_return_val_if_fail (IDE_IS_GRID (self), NULL);
if (nth < 0)
{
- column = ide_layout_grid_create_column (self);
+ column = ide_grid_create_column (self);
gtk_container_add_with_properties (GTK_CONTAINER (self), column,
"index", 0,
NULL);
}
else if (nth >= dzl_multi_paned_get_n_children (DZL_MULTI_PANED (self)))
{
- column = ide_layout_grid_create_column (self);
+ column = ide_grid_create_column (self);
gtk_container_add (GTK_CONTAINER (self), column);
}
else
@@ -1004,13 +1006,13 @@ ide_layout_grid_get_nth_column (IdeLayoutGrid *self,
column = dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (self), nth);
}
- g_return_val_if_fail (IDE_IS_LAYOUT_GRID_COLUMN (column), NULL);
+ g_return_val_if_fail (IDE_IS_GRID_COLUMN (column), NULL);
- return IDE_LAYOUT_GRID_COLUMN (column);
+ return IDE_GRID_COLUMN (column);
}
/*
- * _ide_layout_grid_get_nth_stack:
+ * _ide_grid_get_nth_stack:
*
* This will get the @nth stack. If it does not yet exist,
* it will be created.
@@ -1020,29 +1022,29 @@ ide_layout_grid_get_nth_column (IdeLayoutGrid *self,
* If nth >= the number of stacks, a new stack will be created
* at the end of the grid.
*
- * Returns: (not nullable) (transfer none): An #IdeLayoutStack.
+ * Returns: (not nullable) (transfer none): An #IdeFrame.
*/
-IdeLayoutStack *
-_ide_layout_grid_get_nth_stack (IdeLayoutGrid *self,
+IdeFrame *
+_ide_grid_get_nth_stack (IdeGrid *self,
gint nth)
{
- IdeLayoutGridColumn *column;
- IdeLayoutStack *stack;
+ IdeGridColumn *column;
+ IdeFrame *stack;
- g_return_val_if_fail (IDE_IS_LAYOUT_GRID (self), NULL);
+ g_return_val_if_fail (IDE_IS_GRID (self), NULL);
- column = ide_layout_grid_get_nth_column (self, nth);
- stack = ide_layout_grid_column_get_current_stack (IDE_LAYOUT_GRID_COLUMN (column));
+ column = ide_grid_get_nth_column (self, nth);
+ stack = ide_grid_column_get_current_stack (IDE_GRID_COLUMN (column));
- g_return_val_if_fail (IDE_IS_LAYOUT_STACK (stack), NULL);
+ g_return_val_if_fail (IDE_IS_FRAME (stack), NULL);
return stack;
}
/**
- * _ide_layout_grid_get_nth_stack_for_column:
- * @self: an #IdeLayoutGrid
- * @column: an #IdeLayoutGridColumn
+ * _ide_grid_get_nth_stack_for_column:
+ * @self: an #IdeGrid
+ * @column: an #IdeGridColumn
* @nth: the index of the column, between -1 and G_MAXINT
*
* This will get the @nth stack within @column. If a matching stack
@@ -1053,31 +1055,31 @@ _ide_layout_grid_get_nth_stack (IdeLayoutGrid *self,
* If @nth is greater-than the number of stacks, then a new stack
* will be created at the bottom.
*
- * Returns: (not nullable) (transfer none): An #IdeLayoutStack.
+ * Returns: (not nullable) (transfer none): An #IdeFrame.
*
* Since: 3.32
*/
-IdeLayoutStack *
-_ide_layout_grid_get_nth_stack_for_column (IdeLayoutGrid *self,
- IdeLayoutGridColumn *column,
+IdeFrame *
+_ide_grid_get_nth_stack_for_column (IdeGrid *self,
+ IdeGridColumn *column,
gint nth)
{
GtkWidget *stack;
- g_return_val_if_fail (IDE_IS_LAYOUT_GRID (self), NULL);
- g_return_val_if_fail (IDE_IS_LAYOUT_GRID_COLUMN (column), NULL);
+ g_return_val_if_fail (IDE_IS_GRID (self), NULL);
+ g_return_val_if_fail (IDE_IS_GRID_COLUMN (column), NULL);
g_return_val_if_fail (gtk_widget_get_parent (GTK_WIDGET (column)) == GTK_WIDGET (self), NULL);
if (nth < 0)
{
- stack = ide_layout_grid_create_stack (self);
+ stack = ide_grid_create_frame (self);
gtk_container_add_with_properties (GTK_CONTAINER (column), stack,
"index", 0,
NULL);
}
else if (nth >= dzl_multi_paned_get_n_children (DZL_MULTI_PANED (column)))
{
- stack = ide_layout_grid_create_stack (self);
+ stack = ide_grid_create_frame (self);
gtk_container_add (GTK_CONTAINER (self), stack);
}
else
@@ -1085,28 +1087,28 @@ _ide_layout_grid_get_nth_stack_for_column (IdeLayoutGrid *self,
stack = dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (column), nth);
}
- g_assert (IDE_IS_LAYOUT_STACK (stack));
+ g_assert (IDE_IS_FRAME (stack));
- return IDE_LAYOUT_STACK (stack);
+ return IDE_FRAME (stack);
}
/**
- * ide_layout_grid_get_current_column:
- * @self: a #IdeLayoutGrid
+ * ide_grid_get_current_column:
+ * @self: a #IdeGrid
*
* Gets the most recently focused column of the grid.
*
- * Returns: (transfer none) (not nullable): An #IdeLayoutGridColumn
+ * Returns: (transfer none) (not nullable): An #IdeGridColumn
*
* Since: 3.32
*/
-IdeLayoutGridColumn *
-ide_layout_grid_get_current_column (IdeLayoutGrid *self)
+IdeGridColumn *
+ide_grid_get_current_column (IdeGrid *self)
{
- IdeLayoutGridPrivate *priv = ide_layout_grid_get_instance_private (self);
+ IdeGridPrivate *priv = ide_grid_get_instance_private (self);
GtkWidget *ret = NULL;
- g_return_val_if_fail (IDE_IS_LAYOUT_GRID (self), NULL);
+ g_return_val_if_fail (IDE_IS_GRID (self), NULL);
if (priv->focus_column.head != NULL)
ret = priv->focus_column.head->data;
@@ -1115,19 +1117,19 @@ ide_layout_grid_get_current_column (IdeLayoutGrid *self)
if (ret == NULL)
{
- ret = ide_layout_grid_create_column (self);
+ ret = ide_grid_create_column (self);
gtk_container_add (GTK_CONTAINER (self), ret);
}
- g_return_val_if_fail (IDE_IS_LAYOUT_GRID_COLUMN (ret), NULL);
+ g_return_val_if_fail (IDE_IS_GRID_COLUMN (ret), NULL);
- return IDE_LAYOUT_GRID_COLUMN (ret);
+ return IDE_GRID_COLUMN (ret);
}
/**
- * ide_layout_grid_set_current_column:
- * @self: an #IdeLayoutGrid
- * @column: (nullable): an #IdeLayoutGridColumn or %NULL
+ * ide_grid_set_current_column:
+ * @self: an #IdeGrid
+ * @column: (nullable): an #IdeGridColumn or %NULL
*
* Sets the current column for the grid. Generally this is automatically
* updated for you when the focus changes within the workbench.
@@ -1137,14 +1139,14 @@ ide_layout_grid_get_current_column (IdeLayoutGrid *self)
* Since: 3.32
*/
void
-ide_layout_grid_set_current_column (IdeLayoutGrid *self,
- IdeLayoutGridColumn *column)
+ide_grid_set_current_column (IdeGrid *self,
+ IdeGridColumn *column)
{
- IdeLayoutGridPrivate *priv = ide_layout_grid_get_instance_private (self);
+ IdeGridPrivate *priv = ide_grid_get_instance_private (self);
GList *iter;
- g_return_if_fail (IDE_IS_LAYOUT_GRID (self));
- g_return_if_fail (!column || IDE_IS_LAYOUT_GRID_COLUMN (column));
+ g_return_if_fail (IDE_IS_GRID (self));
+ g_return_if_fail (!column || IDE_IS_GRID_COLUMN (column));
if (column == NULL)
return;
@@ -1160,7 +1162,7 @@ ide_layout_grid_set_current_column (IdeLayoutGrid *self,
g_queue_unlink (&priv->focus_column, iter);
g_queue_push_head_link (&priv->focus_column, iter);
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CURRENT_COLUMN]);
- ide_layout_grid_update_actions (self);
+ ide_grid_update_actions (self);
return;
}
@@ -1169,61 +1171,61 @@ ide_layout_grid_set_current_column (IdeLayoutGrid *self,
}
/**
- * ide_layout_grid_get_current_view:
- * @self: a #IdeLayoutGrid
+ * ide_grid_get_current_page:
+ * @self: a #IdeGrid
*
- * Gets the most recent view used by the user as determined by tracking
+ * Gets the most recent page used by the user as determined by tracking
* the window focus.
*
- * Returns: (transfer none): An #IdeLayoutView or %NULL
+ * Returns: (transfer none): An #IdePage or %NULL
*
* Since: 3.32
*/
-IdeLayoutView *
-ide_layout_grid_get_current_view (IdeLayoutGrid *self)
+IdePage *
+ide_grid_get_current_page (IdeGrid *self)
{
- IdeLayoutStack *stack;
+ IdeFrame *stack;
- g_return_val_if_fail (IDE_IS_LAYOUT_GRID (self), NULL);
+ g_return_val_if_fail (IDE_IS_GRID (self), NULL);
- stack = ide_layout_grid_get_current_stack (self);
+ stack = ide_grid_get_current_stack (self);
if (stack != NULL)
- return ide_layout_stack_get_visible_child (stack);
+ return ide_frame_get_visible_child (stack);
return NULL;
}
static void
-collect_views (GtkWidget *widget,
+collect_pages (GtkWidget *widget,
GPtrArray *ar)
{
- if (IDE_IS_LAYOUT_VIEW (widget))
+ if (IDE_IS_PAGE (widget))
g_ptr_array_add (ar, widget);
}
/**
- * ide_layout_grid_foreach_view:
- * @self: a #IdeLayoutGrid
- * @callback: (scope call) (closure user_data): A callback for each view
+ * ide_grid_foreach_page:
+ * @self: a #IdeGrid
+ * @callback: (scope call) (closure user_data): A callback for each page
* @user_data: user data for @callback
*
- * This function will call @callback for every view found in @self.
+ * This function will call @callback for every page found in @self.
*
* Since: 3.32
*/
void
-ide_layout_grid_foreach_view (IdeLayoutGrid *self,
- GtkCallback callback,
- gpointer user_data)
+ide_grid_foreach_page (IdeGrid *self,
+ GtkCallback callback,
+ gpointer user_data)
{
- g_autoptr(GPtrArray) views = NULL;
+ g_autoptr(GPtrArray) pages = NULL;
guint n_columns;
- g_return_if_fail (IDE_IS_LAYOUT_GRID (self));
+ g_return_if_fail (IDE_IS_GRID (self));
g_return_if_fail (callback != NULL);
- views = g_ptr_array_new ();
+ pages = g_ptr_array_new ();
n_columns = dzl_multi_paned_get_n_children (DZL_MULTI_PANED (self));
@@ -1232,7 +1234,7 @@ ide_layout_grid_foreach_view (IdeLayoutGrid *self,
GtkWidget *column = dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (self), i);
guint n_stacks;
- g_assert (IDE_IS_LAYOUT_GRID_COLUMN (column));
+ g_assert (IDE_IS_GRID_COLUMN (column));
n_stacks = dzl_multi_paned_get_n_children (DZL_MULTI_PANED (column));
@@ -1240,32 +1242,32 @@ ide_layout_grid_foreach_view (IdeLayoutGrid *self,
{
GtkWidget *stack = dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (column), j);
- g_assert (IDE_IS_LAYOUT_STACK (stack));
+ g_assert (IDE_IS_FRAME (stack));
- ide_layout_stack_foreach_view (IDE_LAYOUT_STACK (stack),
- (GtkCallback) collect_views,
- views);
+ ide_frame_foreach_page (IDE_FRAME (stack),
+ (GtkCallback) collect_pages,
+ pages);
}
}
- for (guint i = 0; i < views->len; i++)
- callback (g_ptr_array_index (views, i), user_data);
+ for (guint i = 0; i < pages->len; i++)
+ callback (g_ptr_array_index (pages, i), user_data);
}
static GType
-ide_layout_grid_get_item_type (GListModel *model)
+ide_grid_get_item_type (GListModel *model)
{
- return IDE_TYPE_LAYOUT_VIEW;
+ return IDE_TYPE_PAGE;
}
static guint
-ide_layout_grid_get_n_items (GListModel *model)
+ide_grid_get_n_items (GListModel *model)
{
- IdeLayoutGrid *self = (IdeLayoutGrid *)model;
- IdeLayoutGridPrivate *priv = ide_layout_grid_get_instance_private (self);
+ IdeGrid *self = (IdeGrid *)model;
+ IdeGridPrivate *priv = ide_grid_get_instance_private (self);
guint n_items = 0;
- g_assert (IDE_IS_LAYOUT_GRID (self));
+ g_assert (IDE_IS_GRID (self));
for (guint i = 0; i < priv->stack_info->len; i++)
n_items += g_array_index (priv->stack_info, StackInfo, i).len;
@@ -1274,14 +1276,14 @@ ide_layout_grid_get_n_items (GListModel *model)
}
static gpointer
-ide_layout_grid_get_item (GListModel *model,
+ide_grid_get_item (GListModel *model,
guint position)
{
- IdeLayoutGrid *self = (IdeLayoutGrid *)model;
- IdeLayoutGridPrivate *priv = ide_layout_grid_get_instance_private (self);
+ IdeGrid *self = (IdeGrid *)model;
+ IdeGridPrivate *priv = ide_grid_get_instance_private (self);
- g_assert (IDE_IS_LAYOUT_GRID (self));
- g_assert (position < ide_layout_grid_get_n_items (model));
+ g_assert (IDE_IS_GRID (self));
+ g_assert (position < ide_grid_get_n_items (model));
for (guint i = 0; i < priv->stack_info->len; i++)
{
@@ -1305,23 +1307,23 @@ ide_layout_grid_get_item (GListModel *model,
static void
list_model_iface_init (GListModelInterface *iface)
{
- iface->get_item_type = ide_layout_grid_get_item_type;
- iface->get_n_items = ide_layout_grid_get_n_items;
- iface->get_item = ide_layout_grid_get_item;
+ iface->get_item_type = ide_grid_get_item_type;
+ iface->get_n_items = ide_grid_get_n_items;
+ iface->get_item = ide_grid_get_item;
}
static void
-ide_layout_grid_stack_items_changed (IdeLayoutGrid *self,
+ide_grid_stack_items_changed (IdeGrid *self,
guint position,
guint removed,
guint added,
- IdeLayoutStack *stack)
+ IdeFrame *stack)
{
- IdeLayoutGridPrivate *priv = ide_layout_grid_get_instance_private (self);
+ IdeGridPrivate *priv = ide_grid_get_instance_private (self);
guint real_position = 0;
- g_assert (IDE_IS_LAYOUT_GRID (self));
- g_assert (IDE_IS_LAYOUT_STACK (stack));
+ g_assert (IDE_IS_GRID (self));
+ g_assert (IDE_IS_FRAME (stack));
for (guint i = 0; i < priv->stack_info->len; i++)
{
@@ -1337,9 +1339,9 @@ ide_layout_grid_stack_items_changed (IdeLayoutGrid *self,
removed,
added);
- ide_object_notify_in_main (G_OBJECT (self), properties [PROP_CURRENT_VIEW]);
+ ide_object_notify_in_main (G_OBJECT (self), properties [PROP_CURRENT_PAGE]);
- ide_layout_grid_queue_cull (self);
+ ide_grid_queue_cull (self);
return;
}
@@ -1352,15 +1354,15 @@ ide_layout_grid_stack_items_changed (IdeLayoutGrid *self,
}
void
-_ide_layout_grid_stack_added (IdeLayoutGrid *self,
- IdeLayoutStack *stack)
+_ide_grid_stack_added (IdeGrid *self,
+ IdeFrame *stack)
{
- IdeLayoutGridPrivate *priv = ide_layout_grid_get_instance_private (self);
+ IdeGridPrivate *priv = ide_grid_get_instance_private (self);
StackInfo info = { 0 };
guint n_items;
- g_return_if_fail (IDE_IS_LAYOUT_GRID (self));
- g_return_if_fail (IDE_IS_LAYOUT_STACK (stack));
+ g_return_if_fail (IDE_IS_GRID (self));
+ g_return_if_fail (IDE_IS_FRAME (stack));
g_return_if_fail (G_IS_LIST_MODEL (stack));
info.stack = stack;
@@ -1370,26 +1372,26 @@ _ide_layout_grid_stack_added (IdeLayoutGrid *self,
g_signal_connect_object (stack,
"items-changed",
- G_CALLBACK (ide_layout_grid_stack_items_changed),
+ G_CALLBACK (ide_grid_stack_items_changed),
self,
G_CONNECT_SWAPPED);
n_items = g_list_model_get_n_items (G_LIST_MODEL (stack));
- ide_layout_grid_stack_items_changed (self, 0, 0, n_items, stack);
+ ide_grid_stack_items_changed (self, 0, 0, n_items, stack);
}
void
-_ide_layout_grid_stack_removed (IdeLayoutGrid *self,
- IdeLayoutStack *stack)
+_ide_grid_stack_removed (IdeGrid *self,
+ IdeFrame *stack)
{
- IdeLayoutGridPrivate *priv = ide_layout_grid_get_instance_private (self);
+ IdeGridPrivate *priv = ide_grid_get_instance_private (self);
guint position = 0;
- g_return_if_fail (IDE_IS_LAYOUT_GRID (self));
- g_return_if_fail (IDE_IS_LAYOUT_STACK (stack));
+ g_return_if_fail (IDE_IS_GRID (self));
+ g_return_if_fail (IDE_IS_FRAME (stack));
g_signal_handlers_disconnect_by_func (stack,
- G_CALLBACK (ide_layout_grid_stack_items_changed),
+ G_CALLBACK (ide_grid_stack_items_changed),
self);
for (guint i = 0; i < priv->stack_info->len; i++)
@@ -1406,55 +1408,55 @@ _ide_layout_grid_stack_removed (IdeLayoutGrid *self,
}
static void
-count_views_cb (GtkWidget *widget,
+count_pages_cb (GtkWidget *widget,
gpointer data)
{
(*(guint *)data)++;
}
guint
-ide_layout_grid_count_views (IdeLayoutGrid *self)
+ide_grid_count_pages (IdeGrid *self)
{
guint count = 0;
- g_return_val_if_fail (IDE_IS_LAYOUT_GRID (self), 0);
+ g_return_val_if_fail (IDE_IS_GRID (self), 0);
- ide_layout_grid_foreach_view (self, count_views_cb, &count);
+ ide_grid_foreach_page (self, count_pages_cb, &count);
return count;
}
/**
- * ide_layout_grid_focus_neighbor:
- * @self: An #IdeLayoutGrid
+ * ide_grid_focus_neighbor:
+ * @self: An #IdeGrid
* @dir: the direction for the focus change
*
- * Attempts to focus a neighbor #IdeLayoutView in the grid based on
+ * Attempts to focus a neighbor #IdePage in the grid based on
* the direction requested.
*
- * If an #IdeLayoutView was focused, it will be returned to the caller.
+ * If an #IdePage was focused, it will be returned to the caller.
*
- * Returns: (transfer none) (nullable): An #IdeLayoutView or %NULL
+ * Returns: (transfer none) (nullable): An #IdePage or %NULL
*
* Since: 3.32
*/
-IdeLayoutView *
-ide_layout_grid_focus_neighbor (IdeLayoutGrid *self,
+IdePage *
+ide_grid_focus_neighbor (IdeGrid *self,
GtkDirectionType dir)
{
- IdeLayoutGridColumn *column;
- IdeLayoutStack *stack;
- IdeLayoutView *view = NULL;
+ IdeGridColumn *column;
+ IdeFrame *stack;
+ IdePage *page = NULL;
guint stack_pos = 0;
guint column_pos = 0;
guint n_children;
- g_return_val_if_fail (IDE_IS_LAYOUT_GRID (self), NULL);
+ g_return_val_if_fail (IDE_IS_GRID (self), NULL);
g_return_val_if_fail (dir <= GTK_DIR_RIGHT, NULL);
- /* Make sure we have a current view and stack */
- if (NULL == (stack = ide_layout_grid_get_current_stack (self)) ||
- NULL == (column = ide_layout_grid_get_current_column (self)))
+ /* Make sure we have a current page and stack */
+ if (NULL == (stack = ide_grid_get_current_stack (self)) ||
+ NULL == (column = ide_grid_get_current_column (self)))
return NULL;
gtk_container_child_get (GTK_CONTAINER (self), GTK_WIDGET (column),
@@ -1471,52 +1473,52 @@ ide_layout_grid_focus_neighbor (IdeLayoutGrid *self,
n_children = dzl_multi_paned_get_n_children (DZL_MULTI_PANED (column));
if (n_children - stack_pos == 1)
return NULL;
- stack = IDE_LAYOUT_STACK (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (column), stack_pos + 1));
- view = ide_layout_stack_get_visible_child (stack);
+ stack = IDE_FRAME (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (column), stack_pos + 1));
+ page = ide_frame_get_visible_child (stack);
break;
case GTK_DIR_RIGHT:
n_children = dzl_multi_paned_get_n_children (DZL_MULTI_PANED (self));
if (n_children - column_pos == 1)
return NULL;
- column = IDE_LAYOUT_GRID_COLUMN (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (self), column_pos +
1));
- stack = IDE_LAYOUT_STACK (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (column), 0));
- view = ide_layout_stack_get_visible_child (stack);
+ column = IDE_GRID_COLUMN (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (self), column_pos + 1));
+ stack = IDE_FRAME (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (column), 0));
+ page = ide_frame_get_visible_child (stack);
break;
case GTK_DIR_UP:
if (stack_pos == 0)
return NULL;
- stack = IDE_LAYOUT_STACK (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (column), stack_pos - 1));
- view = ide_layout_stack_get_visible_child (stack);
+ stack = IDE_FRAME (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (column), stack_pos - 1));
+ page = ide_frame_get_visible_child (stack);
break;
case GTK_DIR_LEFT:
if (column_pos == 0)
return NULL;
- column = IDE_LAYOUT_GRID_COLUMN (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (self), column_pos -
1));
- stack = IDE_LAYOUT_STACK (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (column), 0));
- view = ide_layout_stack_get_visible_child (stack);
+ column = IDE_GRID_COLUMN (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (self), column_pos - 1));
+ stack = IDE_FRAME (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (column), 0));
+ page = ide_frame_get_visible_child (stack);
break;
case GTK_DIR_TAB_FORWARD:
- if (!ide_layout_grid_focus_neighbor (self, GTK_DIR_DOWN) &&
- !ide_layout_grid_focus_neighbor (self, GTK_DIR_RIGHT))
+ if (!ide_grid_focus_neighbor (self, GTK_DIR_DOWN) &&
+ !ide_grid_focus_neighbor (self, GTK_DIR_RIGHT))
{
- column = IDE_LAYOUT_GRID_COLUMN (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (self), 0));
- stack = IDE_LAYOUT_STACK (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (column), 0));
- view = ide_layout_stack_get_visible_child (stack);
+ column = IDE_GRID_COLUMN (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (self), 0));
+ stack = IDE_FRAME (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (column), 0));
+ page = ide_frame_get_visible_child (stack);
}
break;
case GTK_DIR_TAB_BACKWARD:
- if (!ide_layout_grid_focus_neighbor (self, GTK_DIR_UP) &&
- !ide_layout_grid_focus_neighbor (self, GTK_DIR_LEFT))
+ if (!ide_grid_focus_neighbor (self, GTK_DIR_UP) &&
+ !ide_grid_focus_neighbor (self, GTK_DIR_LEFT))
{
n_children = dzl_multi_paned_get_n_children (DZL_MULTI_PANED (self));
- column = IDE_LAYOUT_GRID_COLUMN (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (self), n_children
- 1));
- stack = IDE_LAYOUT_STACK (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (column), 0));
- view = ide_layout_stack_get_visible_child (stack);
+ column = IDE_GRID_COLUMN (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (self), n_children - 1));
+ stack = IDE_FRAME (dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (column), 0));
+ page = ide_frame_get_visible_child (stack);
}
break;
@@ -1524,8 +1526,8 @@ ide_layout_grid_focus_neighbor (IdeLayoutGrid *self,
g_assert_not_reached ();
}
- if (view != NULL)
- gtk_widget_child_focus (GTK_WIDGET (view), GTK_DIR_TAB_FORWARD);
+ if (page != NULL)
+ gtk_widget_child_focus (GTK_WIDGET (page), GTK_DIR_TAB_FORWARD);
- return view;
+ return page;
}
diff --git a/src/libide/gui/ide-grid.h b/src/libide/gui/ide-grid.h
new file mode 100644
index 000000000..04516e6b5
--- /dev/null
+++ b/src/libide/gui/ide-grid.h
@@ -0,0 +1,77 @@
+/* ide-grid.h
+ *
+ * Copyright 2017-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#if !defined (IDE_GUI_INSIDE) && !defined (IDE_GUI_COMPILATION)
+# error "Only <libide-gui.h> can be included directly."
+#endif
+
+#include <dazzle.h>
+#include <libide-core.h>
+
+#include "ide-grid-column.h"
+#include "ide-frame.h"
+#include "ide-page.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_GRID (ide_grid_get_type())
+
+IDE_AVAILABLE_IN_3_32
+G_DECLARE_DERIVABLE_TYPE (IdeGrid, ide_grid, IDE, GRID, DzlMultiPaned)
+
+struct _IdeGridClass
+{
+ DzlMultiPanedClass parent_class;
+
+ IdeFrame *(*create_frame) (IdeGrid *self);
+ IdePage *(*create_page) (IdeGrid *self,
+ const gchar *uri);
+
+ /*< private >*/
+ gpointer _reserved[16];
+};
+
+IDE_AVAILABLE_IN_3_32
+GtkWidget *ide_grid_new (void);
+IDE_AVAILABLE_IN_3_32
+IdeGridColumn *ide_grid_get_nth_column (IdeGrid *self,
+ gint nth);
+IDE_AVAILABLE_IN_3_32
+IdePage *ide_grid_focus_neighbor (IdeGrid *self,
+ GtkDirectionType dir);
+IDE_AVAILABLE_IN_3_32
+IdeGridColumn *ide_grid_get_current_column (IdeGrid *self);
+IDE_AVAILABLE_IN_3_32
+void ide_grid_set_current_column (IdeGrid *self,
+ IdeGridColumn *column);
+IDE_AVAILABLE_IN_3_32
+IdeFrame *ide_grid_get_current_stack (IdeGrid *self);
+IDE_AVAILABLE_IN_3_32
+IdePage *ide_grid_get_current_page (IdeGrid *self);
+IDE_AVAILABLE_IN_3_32
+guint ide_grid_count_pages (IdeGrid *self);
+IDE_AVAILABLE_IN_3_32
+void ide_grid_foreach_page (IdeGrid *self,
+ GtkCallback callback,
+ gpointer user_data);
+
+G_END_DECLS
diff --git a/src/libide/util/ide-gtk.c b/src/libide/gui/ide-gui-global.c
similarity index 52%
rename from src/libide/util/ide-gtk.c
rename to src/libide/gui/ide-gui-global.c
index 03c42c60b..bf4063367 100644
--- a/src/libide/util/ide-gtk.c
+++ b/src/libide/gui/ide-gui-global.c
@@ -1,6 +1,6 @@
-/* ide-gtk.c
+/* ide-gui-global.c
*
- * Copyright 2015-2019 Christian Hergert <chergert redhat com>
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,21 +18,27 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
-#define G_LOG_DOMAIN "ide-gtk"
+#define G_LOG_DOMAIN "ide-gui-global"
#include "config.h"
-#include <gtk/gtk.h>
+#include <dazzle.h>
+#include <libide-threading.h>
-#include "application/ide-application.h"
-#include "subprocess/ide-subprocess.h"
-#include "subprocess/ide-subprocess-launcher.h"
-#include "util/ide-flatpak.h"
-#include "util/ide-gtk.h"
+#include "ide-gui-global.h"
+#include "ide-gui-private.h"
+#include "ide-workspace.h"
static GQuark quark_handler;
static GQuark quark_where_context_was;
+static void ide_widget_notify_context (GtkWidget *toplevel,
+ GParamSpec *pspec,
+ GtkWidget *widget);
+static void ide_widget_hierarchy_changed (GtkWidget *widget,
+ GtkWidget *previous_toplevel,
+ gpointer user_data);
+
static void
ide_widget_notify_context (GtkWidget *toplevel,
GParamSpec *pspec,
@@ -58,6 +64,13 @@ ide_widget_notify_context (GtkWidget *toplevel,
g_object_set_qdata (G_OBJECT (widget), quark_where_context_was, context);
+ g_signal_handlers_disconnect_by_func (toplevel,
+ G_CALLBACK (ide_widget_notify_context),
+ widget);
+ g_signal_handlers_disconnect_by_func (widget,
+ G_CALLBACK (ide_widget_hierarchy_changed),
+ NULL);
+
handler (widget, context);
}
@@ -119,6 +132,7 @@ ide_widget_set_context_handler (gpointer widget,
/* Ensure we have our quarks for quick key lookup */
if G_UNLIKELY (quark_handler == 0)
quark_handler = g_quark_from_static_string ("IDE_CONTEXT_HANDLER");
+
if G_UNLIKELY (quark_where_context_was == 0)
quark_where_context_was = g_quark_from_static_string ("IDE_CONTEXT");
@@ -136,113 +150,136 @@ ide_widget_set_context_handler (gpointer widget,
}
/**
- * ide_widget_get_workbench:
+ * ide_widget_get_context:
+ * @widget: a #GtkWidget
*
- * Gets the workbench @widget is associated with, if any.
+ * Gets the context for the widget.
*
- * If no workbench is associated, NULL is returned.
+ * Returns: (nullable) (transfer none): an #IdeContext, or %NULL
*
- * Returns: (transfer none) (nullable): An #IdeWorkbench
+ * Since: 3.32
+ */
+IdeContext *
+ide_widget_get_context (GtkWidget *widget)
+{
+ GtkWidget *toplevel;
+
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+
+ toplevel = gtk_widget_get_toplevel (widget);
+
+ if (IDE_IS_WORKSPACE (toplevel))
+ return ide_workspace_get_context (IDE_WORKSPACE (toplevel));
+
+ return NULL;
+}
+
+/**
+ * ide_widget_get_workbench:
+ * @widget: a #GtkWidget
+ *
+ * Gets the #IdeWorkbench that contains @widget.
+ *
+ * Returns: (transfer none) (nullable): an #IdeWorkbench or %NULL
*
* Since: 3.32
*/
IdeWorkbench *
ide_widget_get_workbench (GtkWidget *widget)
{
- GtkWidget *ancestor;
+ GtkWidget *toplevel;
g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
- if (IDE_IS_WORKBENCH (widget))
- return IDE_WORKBENCH (widget);
+ toplevel = gtk_widget_get_toplevel (widget);
- ancestor = gtk_widget_get_ancestor (widget, IDE_TYPE_WORKBENCH);
- if (IDE_IS_WORKBENCH (ancestor))
- return IDE_WORKBENCH (ancestor);
+ if (GTK_IS_WINDOW (toplevel))
+ {
+ GtkWindowGroup *group = gtk_window_get_group (GTK_WINDOW (toplevel));
- /*
- * TODO: Add "IDE_WORKBENCH" gdata for popout windows.
- */
+ if (IDE_IS_WORKBENCH (group))
+ return IDE_WORKBENCH (group);
+ }
return NULL;
}
/**
- * ide_widget_get_context: (skip)
+ * ide_widget_get_workspace:
+ * @widget: a #GtkWidget
*
- * Returns: (nullable) (transfer none): An #IdeContext or %NULL.
+ * Gets the #IdeWorkspace containing @widget.
+ *
+ * Returns: (transfer none) (nullable): an #IdeWorkspace or %NULL
*
* Since: 3.32
*/
-IdeContext *
-ide_widget_get_context (GtkWidget *widget)
+IdeWorkspace *
+ide_widget_get_workspace (GtkWidget *widget)
{
- IdeWorkbench *workbench;
-
g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
- workbench = ide_widget_get_workbench (widget);
+ return (IdeWorkspace *)dzl_gtk_widget_get_relative (widget, IDE_TYPE_WORKSPACE);
+}
+
+static gboolean
+ide_gtk_progress_bar_tick_cb (gpointer data)
+{
+ GtkProgressBar *progress = data;
+
+ g_assert (GTK_IS_PROGRESS_BAR (progress));
- if (workbench == NULL)
- return NULL;
+ gtk_progress_bar_pulse (progress);
+ gtk_widget_queue_draw (GTK_WIDGET (progress));
- return ide_workbench_get_context (workbench);
+ return G_SOURCE_CONTINUE;
}
void
-ide_widget_message (gpointer instance,
- const gchar *format,
- ...)
+_ide_gtk_progress_bar_stop_pulsing (GtkProgressBar *progress)
{
- g_autofree gchar *str = NULL;
- IdeContext *context = NULL;
- va_list args;
+ guint tick_id;
- g_return_if_fail (IDE_IS_MAIN_THREAD ());
- g_return_if_fail (!instance || GTK_IS_WIDGET (instance));
+ g_return_if_fail (GTK_IS_PROGRESS_BAR (progress));
- va_start (args, format);
- str = g_strdup_vprintf (format, args);
- va_end (args);
+ tick_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (progress), "PULSE_ID"));
- if (instance != NULL)
- context = ide_widget_get_context (instance);
+ if (tick_id != 0)
+ {
+ g_source_remove (tick_id);
+ g_object_set_data (G_OBJECT (progress), "PULSE_ID", NULL);
+ }
- if (context != NULL)
- ide_context_emit_log (context, G_LOG_LEVEL_MESSAGE, str, -1);
- else
- g_message ("%s", str);
+ gtk_progress_bar_set_fraction (progress, 0.0);
}
void
-ide_widget_warning (gpointer instance,
- const gchar *format,
- ...)
+_ide_gtk_progress_bar_start_pulsing (GtkProgressBar *progress)
{
- g_autofree gchar *str = NULL;
- IdeContext *context = NULL;
- va_list args;
-
- g_return_if_fail (IDE_IS_MAIN_THREAD ());
- g_return_if_fail (!instance || GTK_IS_WIDGET (instance));
+ guint tick_id;
- va_start (args, format);
- str = g_strdup_vprintf (format, args);
- va_end (args);
+ g_return_if_fail (GTK_IS_PROGRESS_BAR (progress));
- if (instance != NULL)
- context = ide_widget_get_context (instance);
+ if (g_object_get_data (G_OBJECT (progress), "PULSE_ID"))
+ return;
- if (context != NULL)
- ide_context_emit_log (context, G_LOG_LEVEL_WARNING, str, -1);
- else
- g_warning ("%s", str);
+ gtk_progress_bar_set_fraction (progress, 0.0);
+ gtk_progress_bar_set_pulse_step (progress, .5);
+
+ /* We want lower than the frame rate, because that is all that is needed */
+ tick_id = dzl_frame_source_add_full (G_PRIORITY_DEFAULT,
+ 2,
+ ide_gtk_progress_bar_tick_cb,
+ g_object_ref (progress),
+ g_object_unref);
+ g_object_set_data (G_OBJECT (progress), "PULSE_ID", GUINT_TO_POINTER (tick_id));
+ ide_gtk_progress_bar_tick_cb (progress);
}
gboolean
ide_gtk_show_uri_on_window (GtkWindow *window,
const gchar *uri,
- guint32 timestamp,
+ gint64 timestamp,
GError **error)
{
g_return_val_if_fail (!window || GTK_IS_WINDOW (window), FALSE);
@@ -270,9 +307,52 @@ ide_gtk_show_uri_on_window (GtkWindow *window,
}
else
{
- if (!gtk_show_uri_on_window (window, uri, gtk_get_current_event_time (), error))
+ /* XXX: Workaround for wayland timestamp issue */
+ if (!gtk_show_uri_on_window (window, uri, timestamp / 1000L, error))
return FALSE;
}
return TRUE;
}
+
+static void
+show_parents (GtkWidget *widget)
+{
+ GtkWidget *workspace;
+ GtkWidget *parent;
+
+ g_assert (GTK_IS_WIDGET (widget));
+
+ workspace = gtk_widget_get_ancestor (widget, IDE_TYPE_WORKSPACE);
+ parent = gtk_widget_get_parent (widget);
+
+ if (DZL_IS_DOCK_REVEALER (widget))
+ dzl_dock_revealer_set_reveal_child (DZL_DOCK_REVEALER (widget), TRUE);
+
+ if (IDE_IS_SURFACE (widget))
+ ide_workspace_set_visible_surface (IDE_WORKSPACE (workspace), IDE_SURFACE (widget));
+
+ if (GTK_IS_STACK (parent))
+ gtk_stack_set_visible_child (GTK_STACK (parent), widget);
+
+ if (parent != NULL)
+ show_parents (parent);
+}
+
+void
+ide_widget_reveal_and_grab (GtkWidget *widget)
+{
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ show_parents (widget);
+ gtk_widget_grab_focus (widget);
+}
+
+void
+ide_gtk_window_present (GtkWindow *window)
+{
+ /* TODO: We need the last event time to do this properly. Until then,
+ * we'll just fake some timing info to workaround wayland issues.
+ */
+ gtk_window_present_with_time (window, g_get_monotonic_time () / 1000L);
+}
diff --git a/src/libide/util/ide-gtk.h b/src/libide/gui/ide-gui-global.h
similarity index 64%
rename from src/libide/util/ide-gtk.h
rename to src/libide/gui/ide-gui-global.h
index f78709b9f..399d9f769 100644
--- a/src/libide/util/ide-gtk.h
+++ b/src/libide/gui/ide-gui-global.h
@@ -1,6 +1,6 @@
-/* ide-gtk.h
+/* ide-gui-global.h
*
- * Copyright 2015-2019 Christian Hergert <chergert redhat com>
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,15 +21,18 @@
#pragma once
#include <gtk/gtk.h>
+#include <libide-core.h>
-#include "ide-version-macros.h"
-
-#include "ide-context.h"
-
-#include "workbench/ide-workbench.h"
+#include "ide-workbench.h"
G_BEGIN_DECLS
+#define ide_widget_warning(instance, format, ...) \
+ G_STMT_START { \
+ IdeContext *context = ide_widget_get_context (GTK_WIDGET (instance)); \
+ ide_context_log (context, G_LOG_LEVEL_WARNING, G_LOG_DOMAIN, format __VA_OPT__(,) __VA_ARGS__); \
+ } G_STMT_END
+
typedef void (*IdeWidgetContextHandler) (GtkWidget *widget,
IdeContext *context);
@@ -39,19 +42,17 @@ void ide_widget_set_context_handler (gpointer widget,
IDE_AVAILABLE_IN_3_32
IdeContext *ide_widget_get_context (GtkWidget *widget);
IDE_AVAILABLE_IN_3_32
-IdeWorkbench *ide_widget_get_workbench (GtkWidget *widget);
+void ide_widget_reveal_and_grab (GtkWidget *widget);
IDE_AVAILABLE_IN_3_32
-void ide_widget_message (gpointer instance,
- const gchar *format,
- ...) G_GNUC_PRINTF (2, 3);
+IdeWorkbench *ide_widget_get_workbench (GtkWidget *widget);
IDE_AVAILABLE_IN_3_32
-void ide_widget_warning (gpointer instance,
- const gchar *format,
- ...) G_GNUC_PRINTF (2, 3);
+IdeWorkspace *ide_widget_get_workspace (GtkWidget *widget);
IDE_AVAILABLE_IN_3_32
gboolean ide_gtk_show_uri_on_window (GtkWindow *window,
const gchar *uri,
- guint32 timestamp,
+ gint64 timestamp,
GError **error);
+IDE_AVAILABLE_IN_3_32
+void ide_gtk_window_present (GtkWindow *window);
G_END_DECLS
diff --git a/src/libide/gui/ide-gui-private.h b/src/libide/gui/ide-gui-private.h
new file mode 100644
index 000000000..634312a3d
--- /dev/null
+++ b/src/libide/gui/ide-gui-private.h
@@ -0,0 +1,103 @@
+/* ide-gui-private.h
+ *
+ * Copyright 2017-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <dazzle.h>
+#include <gtk/gtk.h>
+#include <libpeas/peas.h>
+#include <libpeas/peas-autocleanups.h>
+#include <libide-core.h>
+#include <libide-projects.h>
+
+#include "ide-frame.h"
+#include "ide-frame-header.h"
+#include "ide-grid.h"
+#include "ide-grid-column.h"
+#include "ide-header-bar.h"
+#include "ide-notification-list-box-row-private.h"
+#include "ide-notification-stack-private.h"
+#include "ide-notification-view-private.h"
+#include "ide-page.h"
+#include "ide-primary-workspace.h"
+#include "ide-shortcut-label-private.h"
+#include "ide-workbench.h"
+#include "ide-workspace.h"
+
+G_BEGIN_DECLS
+
+void _ide_frame_init_actions (IdeFrame *self);
+void _ide_frame_init_shortcuts (IdeFrame *self);
+void _ide_frame_update_actions (IdeFrame *self);
+void _ide_frame_transfer (IdeFrame *self,
+ IdeFrame *dest,
+ IdePage *view);
+void _ide_grid_column_init_actions (IdeGridColumn *self);
+void _ide_grid_column_update_actions (IdeGridColumn *self);
+gboolean _ide_grid_column_is_empty (IdeGridColumn *self);
+void _ide_grid_column_try_close (IdeGridColumn *self);
+IdeFrame *_ide_grid_get_nth_stack (IdeGrid *self,
+ gint nth);
+IdeFrame *_ide_grid_get_nth_stack_for_column (IdeGrid *self,
+ IdeGridColumn *column,
+ gint nth);
+void _ide_grid_init_actions (IdeGrid *self);
+void _ide_grid_stack_added (IdeGrid *self,
+ IdeFrame *stack);
+void _ide_grid_stack_removed (IdeGrid *self,
+ IdeFrame *stack);
+void _ide_frame_request_close (IdeFrame *stack,
+ IdePage *view);
+void _ide_frame_header_update (IdeFrameHeader *self,
+ IdePage *view);
+void _ide_frame_header_focus_list (IdeFrameHeader *self);
+void _ide_frame_header_hide (IdeFrameHeader *self);
+void _ide_frame_header_popdown (IdeFrameHeader *self);
+void _ide_frame_header_set_pages (IdeFrameHeader *self,
+ GListModel *model);
+void _ide_frame_header_set_title (IdeFrameHeader *self,
+ const gchar *title);
+void _ide_frame_header_set_modified (IdeFrameHeader *self,
+ gboolean modified);
+void _ide_frame_header_set_background_rgba (IdeFrameHeader *self,
+ const GdkRGBA *background_rgba);
+void _ide_frame_header_set_foreground_rgba (IdeFrameHeader *self,
+ const GdkRGBA *foreground_rgba);
+void _ide_primary_workspace_init_actions (IdePrimaryWorkspace *self);
+void _ide_workspace_init_actions (IdeWorkspace *self);
+GList *_ide_workspace_get_mru_link (IdeWorkspace *self);
+void _ide_workspace_add_page_mru (IdeWorkspace *self,
+ GList *mru_link);
+void _ide_workspace_remove_page_mru (IdeWorkspace *self,
+ GList *mru_link);
+void _ide_workspace_move_front_page_mru (IdeWorkspace *workspace,
+ GList *mru_link);
+void _ide_workspace_set_context (IdeWorkspace *workspace,
+ IdeContext *context);
+gboolean _ide_workbench_is_last_workspace (IdeWorkbench *self,
+ IdeWorkspace *workspace);
+void _ide_header_bar_init_shortcuts (IdeHeaderBar *self);
+void _ide_header_bar_show_menu (IdeHeaderBar *self);
+void _ide_gtk_progress_bar_start_pulsing (GtkProgressBar *progress);
+void _ide_gtk_progress_bar_stop_pulsing (GtkProgressBar *progress);
+void _ide_surface_set_fullscreen (IdeSurface *self,
+ gboolean fullscreen);
+
+G_END_DECLS
diff --git a/src/libide/gui/ide-header-bar-shortcuts.c b/src/libide/gui/ide-header-bar-shortcuts.c
new file mode 100644
index 000000000..a0a3230ec
--- /dev/null
+++ b/src/libide/gui/ide-header-bar-shortcuts.c
@@ -0,0 +1,68 @@
+/* ide-header-bar-shortcuts.c
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-header-bar-shortcuts"
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include "ide-gui-private.h"
+
+#define I_(s) (g_intern_static_string(s))
+
+static DzlShortcutEntry workspace_shortcuts[] = {
+ { "org.gnome.builder.workspace.show-menu",
+ 0, NULL,
+ NC_("shortcut window", "Window shortcuts"),
+ NC_("shortcut window", "General"),
+ NC_("shortcut window", "Show window menu") },
+
+ { "org.gnome.builder.workspace.fullscreen",
+ 0, NULL,
+ NC_("shortcut window", "Window shortcuts"),
+ NC_("shortcut window", "General"),
+ NC_("shortcut window", "Toggle window to fullscreen") },
+};
+
+void
+_ide_header_bar_init_shortcuts (IdeHeaderBar *self)
+{
+ DzlShortcutController *controller;
+
+ controller = dzl_shortcut_controller_find (GTK_WIDGET (self));
+
+ dzl_shortcut_controller_add_command_action (controller,
+ I_("org.gnome.builder.workspace.show-menu"),
+ "F10",
+ DZL_SHORTCUT_PHASE_BUBBLE | DZL_SHORTCUT_PHASE_GLOBAL,
+ I_("win.show-menu"));
+
+ dzl_shortcut_controller_add_command_action (controller,
+ I_("org.gnome.builder.workspace.fullscreen"),
+ "F11",
+ DZL_SHORTCUT_PHASE_DISPATCH | DZL_SHORTCUT_PHASE_GLOBAL,
+ I_("win.fullscreen"));
+
+ dzl_shortcut_manager_add_shortcut_entries (NULL,
+ workspace_shortcuts,
+ G_N_ELEMENTS (workspace_shortcuts),
+ GETTEXT_PACKAGE);
+}
diff --git a/src/libide/gui/ide-header-bar.c b/src/libide/gui/ide-header-bar.c
new file mode 100644
index 000000000..245eb5c5e
--- /dev/null
+++ b/src/libide/gui/ide-header-bar.c
@@ -0,0 +1,469 @@
+/* ide-header-bar.c
+ *
+ * Copyright 2014-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-header-bar"
+
+#include "config.h"
+
+#include <dazzle.h>
+
+#include "ide-gui-private.h"
+#include "ide-header-bar.h"
+
+typedef struct
+{
+ gchar *menu_id;
+
+ GtkToggleButton *fullscreen_button;
+ GtkImage *fullscreen_image;
+ DzlShortcutTooltip *fullscreen_tooltip;
+ DzlMenuButton *menu_button;
+ DzlShortcutTooltip *menu_tooltip;
+ GtkBox *primary;
+ GtkBox *secondary;
+
+ guint show_fullscreen_button : 1;
+} IdeHeaderBarPrivate;
+
+enum {
+ PROP_0,
+ PROP_MENU_ID,
+ PROP_SHOW_FULLSCREEN_BUTTON,
+ N_PROPS
+};
+
+static void buildable_iface_init (GtkBuildableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (IdeHeaderBar, ide_header_bar, GTK_TYPE_HEADER_BAR,
+ G_ADD_PRIVATE (IdeHeaderBar)
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, buildable_iface_init))
+
+static GParamSpec *properties [N_PROPS];
+static GtkBuildableIface *buildable_parent;
+
+static void
+on_fullscreen_toggled_cb (GtkToggleButton *button,
+ GParamSpec *pspec,
+ GtkImage *image)
+{
+ const gchar *icon_name;
+
+ g_assert (GTK_IS_TOGGLE_BUTTON (button));
+ g_assert (GTK_IS_IMAGE (image));
+
+ if (gtk_toggle_button_get_active (button))
+ icon_name = "view-restore-symbolic";
+ else
+ icon_name = "view-fullscreen-symbolic";
+
+ g_object_set (image, "icon-name", icon_name, NULL);
+}
+
+static void
+ide_header_bar_finalize (GObject *object)
+{
+ IdeHeaderBar *self = (IdeHeaderBar *)object;
+ IdeHeaderBarPrivate *priv = ide_header_bar_get_instance_private (self);
+
+ g_clear_pointer (&priv->menu_id, g_free);
+
+ G_OBJECT_CLASS (ide_header_bar_parent_class)->finalize (object);
+}
+
+static void
+ide_header_bar_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdeHeaderBar *self = IDE_HEADER_BAR (object);
+
+ switch (prop_id)
+ {
+ case PROP_MENU_ID:
+ g_value_set_string (value, ide_header_bar_get_menu_id (self));
+ break;
+
+ case PROP_SHOW_FULLSCREEN_BUTTON:
+ g_value_set_boolean (value, ide_header_bar_get_show_fullscreen_button (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_header_bar_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdeHeaderBar *self = IDE_HEADER_BAR (object);
+
+ switch (prop_id)
+ {
+ case PROP_MENU_ID:
+ ide_header_bar_set_menu_id (self, g_value_get_string (value));
+ break;
+
+ case PROP_SHOW_FULLSCREEN_BUTTON:
+ ide_header_bar_set_show_fullscreen_button (self, g_value_get_boolean (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_header_bar_class_init (IdeHeaderBarClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = ide_header_bar_finalize;
+ object_class->get_property = ide_header_bar_get_property;
+ object_class->set_property = ide_header_bar_set_property;
+
+ properties [PROP_SHOW_FULLSCREEN_BUTTON] =
+ g_param_spec_boolean ("show-fullscreen-button",
+ "Show Fullscreen Button",
+ "If the fullscreen button should be shown",
+ FALSE,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_MENU_ID] =
+ g_param_spec_string ("menu-id",
+ "Menu ID",
+ "The id of the menu to display with the window",
+ NULL,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/libide-gui/ui/ide-header-bar.ui");
+ gtk_widget_class_bind_template_child_private (widget_class, IdeHeaderBar, fullscreen_button);
+ gtk_widget_class_bind_template_child_private (widget_class, IdeHeaderBar, fullscreen_image);
+ gtk_widget_class_bind_template_child_private (widget_class, IdeHeaderBar, fullscreen_tooltip);
+ gtk_widget_class_bind_template_child_private (widget_class, IdeHeaderBar, menu_button);
+ gtk_widget_class_bind_template_child_private (widget_class, IdeHeaderBar, menu_tooltip);
+ gtk_widget_class_bind_template_child_private (widget_class, IdeHeaderBar, primary);
+ gtk_widget_class_bind_template_child_private (widget_class, IdeHeaderBar, secondary);
+
+ g_type_ensure (DZL_TYPE_PRIORITY_BOX);
+ g_type_ensure (DZL_TYPE_SHORTCUT_TOOLTIP);
+}
+
+static void
+ide_header_bar_init (IdeHeaderBar *self)
+{
+ IdeHeaderBarPrivate *priv = ide_header_bar_get_instance_private (self);
+
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ g_signal_connect_object (priv->fullscreen_button,
+ "notify::active",
+ G_CALLBACK (on_fullscreen_toggled_cb),
+ priv->fullscreen_image,
+ 0);
+
+ _ide_header_bar_init_shortcuts (self);
+}
+
+GtkWidget *
+ide_header_bar_new (void)
+{
+ return g_object_new (IDE_TYPE_HEADER_BAR, NULL);
+}
+
+/**
+ * ide_header_bar_get_show_fullscreen_button:
+ * @self: a #IdeHeaderBar
+ *
+ * Gets if the fullscreen button should be displayed in the header bar.
+ *
+ * Returns: %TRUE if it should be displayed
+ *
+ * Since: 3.32
+ */
+gboolean
+ide_header_bar_get_show_fullscreen_button (IdeHeaderBar *self)
+{
+ IdeHeaderBarPrivate *priv = ide_header_bar_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_HEADER_BAR (self), FALSE);
+
+ return priv->show_fullscreen_button;
+}
+
+/**
+ * ide_header_bar_set_show_fullscreen_button:
+ * @self: a #IdeHeaderBar
+ * @show_fullscreen_button: if the fullscreen button should be displayed
+ *
+ * Changes the visibility of the fullscreen button.
+ *
+ * Since: 3.32
+ */
+void
+ide_header_bar_set_show_fullscreen_button (IdeHeaderBar *self,
+ gboolean show_fullscreen_button)
+{
+ IdeHeaderBarPrivate *priv = ide_header_bar_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_HEADER_BAR (self));
+
+ show_fullscreen_button = !!show_fullscreen_button;
+
+ if (show_fullscreen_button != priv->show_fullscreen_button)
+ {
+ const gchar *session;
+
+ priv->show_fullscreen_button = show_fullscreen_button;
+
+ session = g_getenv ("DESKTOP_SESSION");
+ if (ide_str_equal0 (session, "pantheon"))
+ show_fullscreen_button = FALSE;
+
+ gtk_widget_set_visible (GTK_WIDGET (priv->fullscreen_button), show_fullscreen_button);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SHOW_FULLSCREEN_BUTTON]);
+ }
+}
+
+/**
+ * ide_header_bar_get_menu_id:
+ * @self: a #IdeHeaderBar
+ *
+ * Gets the menu-id to show in the workspace window.
+ *
+ * Returns: (nullable): a string containing the menu-id, or %NULL
+ *
+ * Since: 3.32
+ */
+const gchar *
+ide_header_bar_get_menu_id (IdeHeaderBar *self)
+{
+ IdeHeaderBarPrivate *priv = ide_header_bar_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_HEADER_BAR (self), NULL);
+
+ return priv->menu_id;
+}
+
+/**
+ * ide_header_bar_set_menu_id:
+ * @self: a #IdeHeaderBar
+ *
+ * Sets the menu-id to display in the window.
+ *
+ * Set to %NULL to hide the workspace menu.
+ *
+ * Since: 3.32
+ */
+void
+ide_header_bar_set_menu_id (IdeHeaderBar *self,
+ const gchar *menu_id)
+{
+ IdeHeaderBarPrivate *priv = ide_header_bar_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_HEADER_BAR (self));
+
+ if (!ide_str_equal0 (menu_id, priv->menu_id))
+ {
+ g_free (priv->menu_id);
+ priv->menu_id = g_strdup (menu_id);
+ g_object_set (priv->menu_button, "menu-id", menu_id, NULL);
+ gtk_widget_set_visible (GTK_WIDGET (priv->menu_button), !ide_str_empty0 (menu_id));
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MENU_ID]);
+ }
+}
+
+/**
+ * ide_header_bar_add_primary:
+ * @self: a #IdeHeaderBar
+ *
+ * Adds a widget to the primary button section of the workspace header.
+ * This is the left, for LTR languages.
+ *
+ * Since: 3.32
+ */
+void
+ide_header_bar_add_primary (IdeHeaderBar *self,
+ GtkWidget *widget)
+{
+ IdeHeaderBarPrivate *priv = ide_header_bar_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_HEADER_BAR (self));
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ gtk_container_add (GTK_CONTAINER (priv->primary), widget);
+}
+
+void
+ide_header_bar_add_center_left (IdeHeaderBar *self,
+ GtkWidget *child)
+{
+ IdeHeaderBarPrivate *priv = ide_header_bar_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_HEADER_BAR (self));
+ g_return_if_fail (GTK_IS_WIDGET (child));
+
+ gtk_container_add_with_properties (GTK_CONTAINER (priv->primary), child,
+ "pack-type", GTK_PACK_END,
+ NULL);
+}
+
+/**
+ * ide_header_bar_add_secondary:
+ * @self: a #IdeHeaderBar
+ *
+ * Adds a widget to the secondary button section of the workspace header.
+ * This is the right, for LTR languages.
+ *
+ * Since: 3.32
+ */
+void
+ide_header_bar_add_secondary (IdeHeaderBar *self,
+ GtkWidget *widget)
+{
+ IdeHeaderBarPrivate *priv = ide_header_bar_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_HEADER_BAR (self));
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ gtk_container_add (GTK_CONTAINER (priv->secondary), widget);
+}
+
+void
+_ide_header_bar_show_menu (IdeHeaderBar *self)
+{
+ IdeHeaderBarPrivate *priv = ide_header_bar_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_HEADER_BAR (self));
+
+ gtk_widget_activate (GTK_WIDGET (priv->menu_button));
+}
+
+static void
+ide_header_bar_add_child (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *child,
+ const gchar *type)
+{
+ IdeHeaderBar *self = (IdeHeaderBar *)buildable;
+ IdeHeaderBarPrivate *priv = ide_header_bar_get_instance_private (self);
+
+ g_assert (IDE_IS_HEADER_BAR (self));
+ g_assert (GTK_IS_BUILDER (builder));
+ g_assert (G_IS_OBJECT (child));
+
+ if (ide_str_equal0 (type, "left-of-center"))
+ {
+ if (GTK_IS_WIDGET (child))
+ {
+ gtk_container_add_with_properties (GTK_CONTAINER (priv->primary), GTK_WIDGET (child),
+ "pack-type", GTK_PACK_END,
+ NULL);
+ return;
+ }
+
+ goto warning;
+ }
+
+ if (ide_str_equal0 (type, "left") || ide_str_equal0 (type, "primary"))
+ {
+ if (GTK_IS_WIDGET (child))
+ {
+ gtk_container_add_with_properties (GTK_CONTAINER (priv->primary), GTK_WIDGET (child),
+ "pack-type", GTK_PACK_START,
+ NULL);
+ return;
+ }
+
+ goto warning;
+ }
+
+ if (ide_str_equal0 (type, "right-of-center"))
+ {
+ if (GTK_IS_WIDGET (child))
+ {
+ gtk_container_add_with_properties (GTK_CONTAINER (priv->secondary), GTK_WIDGET (child),
+ "pack-type", GTK_PACK_START,
+ NULL);
+ return;
+ }
+
+ goto warning;
+ }
+
+ if (ide_str_equal0 (type, "right") || ide_str_equal0 (type, "secondary"))
+ {
+ if (GTK_IS_WIDGET (child))
+ {
+ gtk_container_add_with_properties (GTK_CONTAINER (priv->secondary), GTK_WIDGET (child),
+ "pack-type", GTK_PACK_END,
+ NULL);
+ return;
+ }
+
+ goto warning;
+ }
+
+ buildable_parent->add_child (buildable, builder, child, type);
+
+ return;
+
+warning:
+ g_warning ("'%s' child type must be a GtkWidget, not %s",
+ type, G_OBJECT_TYPE_NAME (child));
+}
+
+static GObject *
+ide_header_bar_get_internal_child (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ const gchar *child_name)
+{
+ IdeHeaderBar *self = (IdeHeaderBar *)buildable;
+ IdeHeaderBarPrivate *priv = ide_header_bar_get_instance_private (self);
+
+ g_assert (IDE_IS_HEADER_BAR (self));
+ g_assert (GTK_IS_BUILDER (builder));
+
+ if (ide_str_equal0 (child_name, "primary"))
+ return G_OBJECT (priv->primary);
+
+ if (ide_str_equal0 (child_name, "secondary"))
+ return G_OBJECT (priv->secondary);
+
+ if (buildable_parent->get_internal_child)
+ return buildable_parent->get_internal_child (buildable, builder, child_name);
+
+ return NULL;
+}
+
+static void
+buildable_iface_init (GtkBuildableIface *iface)
+{
+ buildable_parent = g_type_interface_peek_parent (iface);
+ iface->add_child = ide_header_bar_add_child;
+ iface->get_internal_child = ide_header_bar_get_internal_child;
+}
diff --git a/src/libide/gui/ide-header-bar.h b/src/libide/gui/ide-header-bar.h
new file mode 100644
index 000000000..ec77fbd39
--- /dev/null
+++ b/src/libide/gui/ide-header-bar.h
@@ -0,0 +1,67 @@
+/* ide-header-bar.h
+ *
+ * Copyright 2014-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#if !defined (IDE_GUI_INSIDE) && !defined (IDE_GUI_COMPILATION)
+# error "Only <libide-gui.h> can be included directly."
+#endif
+
+#include <gtk/gtk.h>
+#include <libide-core.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_HEADER_BAR (ide_header_bar_get_type())
+
+IDE_AVAILABLE_IN_3_32
+G_DECLARE_DERIVABLE_TYPE (IdeHeaderBar, ide_header_bar, IDE, HEADER_BAR, GtkHeaderBar)
+
+struct _IdeHeaderBarClass
+{
+ GtkHeaderBarClass parent_class;
+
+ /*< private >*/
+ gpointer _reserved[16];
+};
+
+IDE_AVAILABLE_IN_3_32
+GtkWidget *ide_header_bar_new (void);
+IDE_AVAILABLE_IN_3_32
+void ide_header_bar_add_primary (IdeHeaderBar *self,
+ GtkWidget *widget);
+IDE_AVAILABLE_IN_3_32
+void ide_header_bar_add_center_left (IdeHeaderBar *self,
+ GtkWidget *widget);
+IDE_AVAILABLE_IN_3_32
+void ide_header_bar_add_secondary (IdeHeaderBar *self,
+ GtkWidget *widget);
+IDE_AVAILABLE_IN_3_32
+const gchar *ide_header_bar_get_menu_id (IdeHeaderBar *self);
+IDE_AVAILABLE_IN_3_32
+void ide_header_bar_set_menu_id (IdeHeaderBar *self,
+ const gchar *menu_id);
+IDE_AVAILABLE_IN_3_32
+gboolean ide_header_bar_get_show_fullscreen_button (IdeHeaderBar *self);
+IDE_AVAILABLE_IN_3_32
+void ide_header_bar_set_show_fullscreen_button (IdeHeaderBar *self,
+ gboolean show_fullscreen_button);
+
+G_END_DECLS
diff --git a/src/libide/gui/ide-header-bar.ui b/src/libide/gui/ide-header-bar.ui
new file mode 100644
index 000000000..e55beb724
--- /dev/null
+++ b/src/libide/gui/ide-header-bar.ui
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.24 -->
+ <template class="IdeHeaderBar" parent="GtkHeaderBar">
+ <child>
+ <object class="DzlPriorityBox" id="primary">
+ <property name="hexpand">true</property>
+ <property name="margin-end">6</property>
+ <property name="orientation">horizontal</property>
+ <property name="spacing">6</property>
+ <property name="visible">true</property>
+ </object>
+ <packing>
+ <property name="pack-type">start</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="DzlPriorityBox" id="secondary">
+ <property name="hexpand">true</property>
+ <property name="margin-start">6</property>
+ <property name="spacing">6</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">horizontal</property>
+ <property name="spacing">6</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkToggleButton" id="fullscreen_button">
+ <property name="action-name">win.fullscreen</property>
+ <property name="focus-on-click">false</property>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child>
+ <object class="GtkImage" id="fullscreen_image">
+ <property name="icon-name">view-fullscreen-symbolic</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="DzlMenuButton" id="menu_button">
+ <property name="icon-name">open-menu-symbolic</property>
+ <property name="show-accels">true</property>
+ <property name="show-icons">true</property>
+ <style>
+ <class name="image-button"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="pack-type">end</property>
+ <property name="priority">-1000000</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="pack-type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </template>
+ <object class="DzlShortcutTooltip" id="fullscreen_tooltip">
+ <property name="command-id">org.gnome.builder.workspace.fullscreen</property>
+ <property name="widget">fullscreen_button</property>
+ </object>
+ <object class="DzlShortcutTooltip" id="menu_tooltip">
+ <property name="command-id">org.gnome.builder.workspace.show-menu</property>
+ <property name="widget">menu_button</property>
+ </object>
+</interface>
+
diff --git a/src/libide/gui/ide-marked-view.c b/src/libide/gui/ide-marked-view.c
new file mode 100644
index 000000000..0edfa8f1f
--- /dev/null
+++ b/src/libide/gui/ide-marked-view.c
@@ -0,0 +1,112 @@
+/* ide-marked-view.c
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-marked-view"
+
+#include "config.h"
+
+#include <webkit2/webkit2.h>
+
+#include "gs-markdown-private.h"
+#include "ide-marked-view.h"
+
+struct _IdeMarkedView
+{
+ GtkBin parent_instance;
+};
+
+G_DEFINE_TYPE (IdeMarkedView, ide_marked_view, GTK_TYPE_BIN)
+
+static void
+ide_marked_view_class_init (IdeMarkedViewClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ gtk_widget_class_set_css_name (widget_class, "markedview");
+}
+
+static void
+ide_marked_view_init (IdeMarkedView *self)
+{
+}
+
+GtkWidget *
+ide_marked_view_new (IdeMarkedContent *content)
+{
+ g_autofree gchar *markup = NULL;
+ GtkWidget *child = NULL;
+ IdeMarkedView *self;
+ IdeMarkedKind kind;
+
+ g_return_val_if_fail (content != NULL, NULL);
+
+ self = g_object_new (IDE_TYPE_MARKED_VIEW, NULL);
+ kind = ide_marked_content_get_kind (content);
+ markup = ide_marked_content_as_string (content);
+
+ switch (kind)
+ {
+ default:
+ case IDE_MARKED_KIND_PLAINTEXT:
+ case IDE_MARKED_KIND_PANGO:
+ child = g_object_new (GTK_TYPE_LABEL,
+ "max-width-chars", 80,
+ "wrap", TRUE,
+ "xalign", 0.0f,
+ "visible", TRUE,
+ "use-markup", kind == IDE_MARKED_KIND_PANGO,
+ "label", markup,
+ NULL);
+ break;
+
+ case IDE_MARKED_KIND_HTML:
+ child = g_object_new (WEBKIT_TYPE_WEB_VIEW,
+ "visible", TRUE,
+ NULL);
+ webkit_web_view_load_html (WEBKIT_WEB_VIEW (child), markup, NULL);
+ break;
+
+ case IDE_MARKED_KIND_MARKDOWN:
+ {
+ g_autoptr(GsMarkdown) md = gs_markdown_new (GS_MARKDOWN_OUTPUT_PANGO);
+ g_autofree gchar *parsed = NULL;
+
+ gs_markdown_set_smart_quoting (md, TRUE);
+ gs_markdown_set_autocode (md, TRUE);
+ gs_markdown_set_autolinkify (md, TRUE);
+
+ if ((parsed = gs_markdown_parse (md, markup)))
+ child = g_object_new (GTK_TYPE_LABEL,
+ "max-width-chars", 80,
+ "wrap", TRUE,
+ "xalign", 0.0f,
+ "visible", TRUE,
+ "use-markup", TRUE,
+ "label", parsed,
+ NULL);
+ }
+ break;
+ }
+
+ if (child != NULL)
+ gtk_container_add (GTK_CONTAINER (self), child);
+
+ return GTK_WIDGET (self);
+}
diff --git a/src/libide/workbench/ide-omni-bar.h b/src/libide/gui/ide-marked-view.h
similarity index 71%
rename from src/libide/workbench/ide-omni-bar.h
rename to src/libide/gui/ide-marked-view.h
index bc8bbd87b..b424b868a 100644
--- a/src/libide/workbench/ide-omni-bar.h
+++ b/src/libide/gui/ide-marked-view.h
@@ -1,6 +1,6 @@
-/* ide-omni-bar.h
+/* ide-marked-view.h
*
- * Copyright 2016-2019 Christian Hergert <chergert redhat com>
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,18 +21,17 @@
#pragma once
#include <gtk/gtk.h>
-
-#include "ide-types.h"
-#include "ide-version-macros.h"
+#include <libide-core.h>
+#include <libide-io.h>
G_BEGIN_DECLS
-#define IDE_TYPE_OMNI_BAR (ide_omni_bar_get_type())
+#define IDE_TYPE_MARKED_VIEW (ide_marked_view_get_type())
IDE_AVAILABLE_IN_3_32
-G_DECLARE_FINAL_TYPE (IdeOmniBar, ide_omni_bar, IDE, OMNI_BAR, GtkBox)
+G_DECLARE_FINAL_TYPE (IdeMarkedView, ide_marked_view, IDE, MARKED_VIEW, GtkBin)
IDE_AVAILABLE_IN_3_32
-GtkWidget *ide_omni_bar_new (void);
+GtkWidget *ide_marked_view_new (IdeMarkedContent *content);
G_END_DECLS
diff --git a/src/libide/gui/ide-notification-list-box-row-private.h
b/src/libide/gui/ide-notification-list-box-row-private.h
new file mode 100644
index 000000000..b0a603ff5
--- /dev/null
+++ b/src/libide/gui/ide-notification-list-box-row-private.h
@@ -0,0 +1,38 @@
+/* ide-notification-list-box-row-private.h
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <libide-core.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_NOTIFICATION_LIST_BOX_ROW (ide_notification_list_box_row_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeNotificationListBoxRow, ide_notification_list_box_row, IDE,
NOTIFICATION_LIST_BOX_ROW, GtkListBoxRow)
+
+GtkWidget *ide_notification_list_box_row_new (IdeNotification *notification);
+IdeNotification *ide_notification_list_box_row_get_notification (IdeNotificationListBoxRow *self);
+void ide_notification_list_box_row_set_compact (IdeNotificationListBoxRow *self,
+ gboolean compact);
+gboolean ide_notification_list_box_row_get_compact (IdeNotificationListBoxRow *self);
+
+G_END_DECLS
diff --git a/src/libide/gui/ide-notification-list-box-row.c b/src/libide/gui/ide-notification-list-box-row.c
new file mode 100644
index 000000000..8bc0ca5fe
--- /dev/null
+++ b/src/libide/gui/ide-notification-list-box-row.c
@@ -0,0 +1,377 @@
+/* ide-notification-list-box-row.c
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-notification-list-box-row"
+
+#include "config.h"
+
+#include <dazzle.h>
+
+#include "ide-gui-private.h"
+#include "ide-notification-list-box-row-private.h"
+
+struct _IdeNotificationListBoxRow
+{
+ GtkListBoxRow parent_instance;
+
+ IdeNotification *notification;
+
+ GtkLabel *body;
+ GtkLabel *title;
+ GtkBox *lower_button_area;
+ GtkBox *side_button_area;
+ GtkBox *buttons;
+ GtkProgressBar *progress;
+
+ guint compact : 1;
+};
+
+G_DEFINE_TYPE (IdeNotificationListBoxRow, ide_notification_list_box_row, GTK_TYPE_LIST_BOX_ROW)
+
+enum {
+ PROP_0,
+ PROP_COMPACT,
+ PROP_NOTIFICATION,
+ N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+setup_buttons_locked (IdeNotificationListBoxRow *self)
+{
+ g_autofree gchar *body = NULL;
+ g_autofree gchar *title = NULL;
+ guint n_buttons;
+
+ g_assert (IDE_IS_NOTIFICATION_LIST_BOX_ROW (self));
+ g_assert (self->notification != NULL);
+
+ title = ide_notification_dup_title (self->notification);
+ body = ide_notification_dup_body (self->notification);
+
+ n_buttons = ide_notification_get_n_buttons (self->notification);
+
+ for (guint i = 0; i < n_buttons; i++)
+ {
+ g_autofree gchar *action = NULL;
+ g_autofree gchar *label = NULL;
+ g_autoptr(GIcon) icon = NULL;
+ g_autoptr(GVariant) target = NULL;
+
+ if (ide_notification_get_button (self->notification, i, &label, &icon, &action, &target))
+ {
+ GtkButton *button;
+ GtkWidget *child = NULL;
+
+ if (action == NULL || (label == NULL && icon == NULL))
+ continue;
+
+ if (label != NULL && (!self->compact || icon == NULL))
+ child = g_object_new (GTK_TYPE_LABEL,
+ "label", label,
+ "visible", TRUE,
+ NULL);
+ else if (icon != NULL)
+ child = g_object_new (GTK_TYPE_IMAGE,
+ "icon-size", GTK_ICON_SIZE_MENU,
+ "gicon", icon,
+ "visible", TRUE,
+ NULL);
+
+ g_assert (GTK_IS_WIDGET (child));
+
+ button = g_object_new (GTK_TYPE_TOGGLE_BUTTON,
+ "child", child,
+ "action-name", action,
+ "action-target", target,
+ "visible", TRUE,
+ NULL);
+
+ if (!self->compact)
+ dzl_gtk_widget_add_style_class (GTK_WIDGET (button), "suggested-action");
+ else
+ dzl_gtk_widget_add_style_class (GTK_WIDGET (button), "circular");
+
+ g_assert (GTK_IS_WIDGET (button));
+
+ gtk_container_add_with_properties (GTK_CONTAINER (self->buttons), GTK_WIDGET (button),
+ "pack-type", GTK_PACK_END,
+ NULL);
+ }
+ }
+
+ /* Always show labels when compact+buttons for alignment. */
+ gtk_widget_set_visible (GTK_WIDGET (self->body),
+ !ide_str_empty0 (body) || (self->compact && n_buttons > 0));
+ gtk_widget_set_visible (GTK_WIDGET (self->title),
+ !ide_str_empty0 (title) || (self->compact && n_buttons > 0));
+
+ gtk_widget_set_visible (GTK_WIDGET (self->buttons), n_buttons > 0);
+}
+
+/**
+ * ide_notification_list_box_row_new:
+ *
+ * Create a new #IdeNotificationListBoxRow.
+ *
+ * Returns: (transfer full): a newly created #IdeNotificationListBoxRow
+ *
+ * Since: 3.32
+ */
+GtkWidget *
+ide_notification_list_box_row_new (IdeNotification *notification)
+{
+ g_return_val_if_fail (IDE_IS_NOTIFICATION (notification), NULL);
+
+ return g_object_new (IDE_TYPE_NOTIFICATION_LIST_BOX_ROW,
+ "notification", notification,
+ NULL);
+}
+
+static void
+ide_notification_list_box_row_constructed (GObject *object)
+{
+ IdeNotificationListBoxRow *self = (IdeNotificationListBoxRow *)object;
+ g_autofree gchar *body = NULL;
+ g_autofree gchar *title = NULL;
+
+ g_assert (IDE_IS_NOTIFICATION_LIST_BOX_ROW (self));
+
+ if (self->notification == NULL)
+ {
+ g_warning ("%s created without an IdeNotification!",
+ G_OBJECT_TYPE_NAME (self));
+ goto chain_up;
+ }
+
+ ide_object_lock (IDE_OBJECT (self->notification));
+
+ body = ide_notification_dup_body (self->notification);
+ title = ide_notification_dup_title (self->notification);
+
+ g_object_bind_property (self->notification, "title", self->title, "label", G_BINDING_SYNC_CREATE);
+ g_object_bind_property (self->notification, "body", self->body, "label", G_BINDING_SYNC_CREATE);
+
+ /* Always show labels when compact+buttons for alignment. */
+ gtk_widget_set_visible (GTK_WIDGET (self->body),
+ !ide_str_empty0 (body) ||
+ (self->compact && ide_notification_get_n_buttons (self->notification)));
+ gtk_widget_set_visible (GTK_WIDGET (self->title),
+ !ide_str_empty0 (title) ||
+ (self->compact && ide_notification_get_n_buttons (self->notification)));
+
+ if (ide_notification_get_urgent (self->notification))
+ dzl_gtk_widget_add_style_class (GTK_WIDGET (self), "needs-attention");
+
+ gtk_widget_set_visible (GTK_WIDGET (self->progress),
+ ide_notification_get_has_progress (self->notification));
+ g_object_bind_property (self->notification, "progress",
+ self->progress, "fraction",
+ G_BINDING_SYNC_CREATE);
+
+ setup_buttons_locked (self);
+
+ if (ide_notification_get_progress_is_imprecise (self->notification))
+ _ide_gtk_progress_bar_start_pulsing (self->progress);
+
+ ide_object_unlock (IDE_OBJECT (self->notification));
+
+chain_up:
+ G_OBJECT_CLASS (ide_notification_list_box_row_parent_class)->constructed (object);
+}
+
+static void
+ide_notification_list_box_row_destroy (GtkWidget *widget)
+{
+ IdeNotificationListBoxRow *self = (IdeNotificationListBoxRow *)widget;
+
+ if (self->progress != NULL)
+ _ide_gtk_progress_bar_stop_pulsing (self->progress);
+
+ g_clear_object (&self->notification);
+
+ GTK_WIDGET_CLASS (ide_notification_list_box_row_parent_class)->destroy (widget);
+}
+
+static void
+ide_notification_list_box_row_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdeNotificationListBoxRow *self = IDE_NOTIFICATION_LIST_BOX_ROW (object);
+
+ switch (prop_id)
+ {
+ case PROP_COMPACT:
+ g_value_set_boolean (value, ide_notification_list_box_row_get_compact (self));
+ break;
+
+ case PROP_NOTIFICATION:
+ g_value_set_object (value, self->notification);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_notification_list_box_row_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdeNotificationListBoxRow *self = IDE_NOTIFICATION_LIST_BOX_ROW (object);
+
+ switch (prop_id)
+ {
+ case PROP_COMPACT:
+ ide_notification_list_box_row_set_compact (self, g_value_get_boolean (value));
+ break;
+
+ case PROP_NOTIFICATION:
+ self->notification = g_value_dup_object (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_notification_list_box_row_class_init (IdeNotificationListBoxRowClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->constructed = ide_notification_list_box_row_constructed;
+ object_class->get_property = ide_notification_list_box_row_get_property;
+ object_class->set_property = ide_notification_list_box_row_set_property;
+
+ widget_class->destroy = ide_notification_list_box_row_destroy;
+
+ properties [PROP_COMPACT] =
+ g_param_spec_boolean ("compact",
+ "Compact",
+ "If the compact button mode should be used",
+ FALSE,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_NOTIFICATION] =
+ g_param_spec_object ("notification",
+ "Notification",
+ "The notification to display",
+ IDE_TYPE_NOTIFICATION,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+
+ gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/libide-gui/ui/ide-notification-list-box-row.ui");
+ gtk_widget_class_bind_template_child (widget_class, IdeNotificationListBoxRow, body);
+ gtk_widget_class_bind_template_child (widget_class, IdeNotificationListBoxRow, buttons);
+ gtk_widget_class_bind_template_child (widget_class, IdeNotificationListBoxRow, lower_button_area);
+ gtk_widget_class_bind_template_child (widget_class, IdeNotificationListBoxRow, progress);
+ gtk_widget_class_bind_template_child (widget_class, IdeNotificationListBoxRow, side_button_area);
+ gtk_widget_class_bind_template_child (widget_class, IdeNotificationListBoxRow, title);
+}
+
+static void
+ide_notification_list_box_row_init (IdeNotificationListBoxRow *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+}
+
+/**
+ * ide_notification_list_box_row_get_notification:
+ * @self: a #IdeNotificationListBoxRow
+ *
+ * Returns: (transfer none) (nullable): an #IdeNotification
+ *
+ * Since: 3.32
+ */
+IdeNotification *
+ide_notification_list_box_row_get_notification (IdeNotificationListBoxRow *self)
+{
+ g_return_val_if_fail (IDE_IS_NOTIFICATION_LIST_BOX_ROW (self), NULL);
+
+ return self->notification;
+}
+
+gboolean
+ide_notification_list_box_row_get_compact (IdeNotificationListBoxRow *self)
+{
+ g_return_val_if_fail (IDE_IS_NOTIFICATION_LIST_BOX_ROW (self), FALSE);
+
+ return self->compact;
+}
+
+void
+ide_notification_list_box_row_set_compact (IdeNotificationListBoxRow *self,
+ gboolean compact)
+{
+ GtkBox *parent;
+
+ g_return_if_fail (IDE_IS_NOTIFICATION_LIST_BOX_ROW (self));
+
+ if (self->compact != compact)
+ {
+ self->compact = compact;
+
+ g_object_ref (self->buttons);
+
+ gtk_container_foreach (GTK_CONTAINER (self->buttons),
+ (GtkCallback)gtk_widget_destroy,
+ NULL);
+
+ parent = GTK_BOX (gtk_widget_get_parent (GTK_WIDGET (self->buttons)));
+ gtk_container_remove (GTK_CONTAINER (parent), GTK_WIDGET (self->buttons));
+ gtk_widget_hide (GTK_WIDGET (parent));
+
+ if (compact)
+ parent = self->side_button_area;
+ else
+ parent = self->lower_button_area;
+
+ gtk_container_add_with_properties (GTK_CONTAINER (parent), GTK_WIDGET (self->buttons),
+ "pack-type", GTK_PACK_END,
+ NULL);
+
+ g_object_unref (self->buttons);
+
+ gtk_label_set_width_chars (self->title, self->compact ? 35 : 50);
+ gtk_label_set_max_width_chars (self->title, self->compact ? 35 : 50);
+
+ gtk_label_set_width_chars (self->body, self->compact ? 35 : 50);
+ gtk_label_set_max_width_chars (self->body, self->compact ? 35 : 50);
+
+ if (self->notification != NULL)
+ {
+ ide_object_lock (IDE_OBJECT (self->notification));
+ setup_buttons_locked (self);
+ gtk_widget_set_visible (GTK_WIDGET (parent),
+ ide_notification_get_n_buttons (self->notification) > 0);
+ ide_object_unlock (IDE_OBJECT (self->notification));
+ }
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_COMPACT]);
+ }
+}
diff --git a/src/libide/gui/ide-notification-list-box-row.ui b/src/libide/gui/ide-notification-list-box-row.ui
new file mode 100644
index 000000000..b74f8eaea
--- /dev/null
+++ b/src/libide/gui/ide-notification-list-box-row.ui
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.0 -->
+<interface>
+ <requires lib="gtk+" version="3.24"/>
+ <template class="IdeNotificationListBoxRow" parent="GtkListBoxRow">
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkGrid" id="grid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+ <property name="baseline_row">2</property>
+ <child>
+ <object class="GtkProgressBar" id="progress">
+ <property name="name">progress</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">baseline</property>
+ <property name="hexpand">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="title">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <style>
+ <class name="title"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="body">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="wrap">True</property>
+ <property name="width_chars">50</property>
+ <property name="max_width_chars">50</property>
+ <property name="xalign">0</property>
+ <style>
+ <class name="body"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="lower_button_area">
+ <property name="margin_top">6</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkBox" id="buttons">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">3</property>
+ <property name="width">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="side_button_area">
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="margin_top">10</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="height">3</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <style>
+ <class name="notification"/>
+ </style>
+ </template>
+</interface>
diff --git a/src/libide/gui/ide-notification-stack-private.h b/src/libide/gui/ide-notification-stack-private.h
new file mode 100644
index 000000000..df9f2e0ca
--- /dev/null
+++ b/src/libide/gui/ide-notification-stack-private.h
@@ -0,0 +1,44 @@
+/* ide-notification-stack-private.h
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+#include <libide-core.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_NOTIFICATION_STACK (ide_notification_stack_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeNotificationStack, ide_notification_stack, IDE, NOTIFICATION_STACK, GtkStack)
+
+GtkWidget *ide_notification_stack_new (void);
+void ide_notification_stack_bind_model (IdeNotificationStack *self,
+ GListModel *notifications);
+gboolean ide_notification_stack_is_empty (IdeNotificationStack *self);
+gboolean ide_notification_stack_get_can_move (IdeNotificationStack *self);
+void ide_notification_stack_move_next (IdeNotificationStack *self);
+void ide_notification_stack_move_previous (IdeNotificationStack *self);
+IdeNotification *ide_notification_stack_get_visible (IdeNotificationStack *self);
+gdouble ide_notification_stack_get_progress (IdeNotificationStack *self);
+void ide_notification_stack_set_progress (IdeNotificationStack *self,
+ gdouble progress);
+
+G_END_DECLS
diff --git a/src/libide/gui/ide-notification-stack.c b/src/libide/gui/ide-notification-stack.c
new file mode 100644
index 000000000..9d33b6f47
--- /dev/null
+++ b/src/libide/gui/ide-notification-stack.c
@@ -0,0 +1,405 @@
+/* ide-notification-stack.c
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-notification-stack"
+
+#include "config.h"
+
+#include <dazzle.h>
+
+#include "ide-notification-stack-private.h"
+#include "ide-notification-view-private.h"
+
+#define CAROUSEL_TIMEOUT_SECS 5
+#define TRANSITION_DURATION 500
+
+struct _IdeNotificationStack
+{
+ GtkStack parent_instance;
+ DzlSignalGroup *signals;
+ DzlBindingGroup *bindings;
+ GListModel *model;
+ gdouble progress;
+ guint carousel_source;
+ guint in_carousel : 1;
+};
+
+enum {
+ PROP_0,
+ PROP_PROGRESS,
+ N_PROPS
+};
+
+enum {
+ CHANGED,
+ N_SIGNALS
+};
+
+G_DEFINE_TYPE (IdeNotificationStack, ide_notification_stack, GTK_TYPE_STACK)
+
+static guint signals [N_SIGNALS];
+static GParamSpec *properties [N_PROPS];
+
+static void
+ide_notification_stack_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdeNotificationStack *self = IDE_NOTIFICATION_STACK (object);
+
+ switch (prop_id)
+ {
+ case PROP_PROGRESS:
+ g_value_set_double (value, ide_notification_stack_get_progress (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_notification_stack_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdeNotificationStack *self = IDE_NOTIFICATION_STACK (object);
+
+ switch (prop_id)
+ {
+ case PROP_PROGRESS:
+ ide_notification_stack_set_progress (self, g_value_get_double (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static gboolean
+ide_notification_stack_carousel_cb (gpointer data)
+{
+ IdeNotificationStack *self = data;
+
+ g_assert (IDE_IS_NOTIFICATION_STACK (self));
+
+ self->in_carousel = TRUE;
+ ide_notification_stack_move_next (self);
+ self->in_carousel = FALSE;
+
+ return G_SOURCE_CONTINUE;
+}
+
+static void
+ide_notification_stack_items_changed_cb (IdeNotificationStack *self,
+ guint position,
+ guint removed,
+ guint added,
+ GListModel *model)
+{
+ GtkWidget *urgent = NULL;
+ GList *children;
+ GList *iter;
+
+ g_assert (IDE_IS_NOTIFICATION_STACK (self));
+
+ children = gtk_container_get_children (GTK_CONTAINER (self));
+ iter = g_list_nth (children, position);
+
+ for (guint i = 0; i < removed; i++, iter = iter->next)
+ {
+ GtkWidget *child = iter->data;
+ gtk_widget_destroy (child);
+ }
+
+ g_list_free (children);
+
+ for (guint i = 0; i < added; i++)
+ {
+ g_autoptr(IdeNotification) notif = g_list_model_get_item (model, position + i);
+ GtkWidget *view = g_object_new (IDE_TYPE_NOTIFICATION_VIEW,
+ "notification", notif,
+ "visible", TRUE,
+ NULL);
+
+ gtk_container_add_with_properties (GTK_CONTAINER (self), view,
+ "position", position + i,
+ NULL);
+
+ if (!urgent && ide_notification_get_urgent (notif))
+ urgent = view;
+ }
+
+ if (urgent != NULL)
+ {
+ gtk_stack_set_visible_child (GTK_STACK (self), urgent);
+ g_clear_handle_id (&self->carousel_source, g_source_remove);
+ }
+
+ if (self->carousel_source == 0 && g_list_model_get_n_items (model))
+ self->carousel_source = g_timeout_add_seconds (CAROUSEL_TIMEOUT_SECS,
+ ide_notification_stack_carousel_cb,
+ self);
+
+ g_signal_emit (self, signals [CHANGED], 0);
+}
+
+static void
+ide_notification_stack_notify_visible_child (IdeNotificationStack *self)
+{
+ g_assert (IDE_IS_NOTIFICATION_STACK (self));
+
+ self->progress = 0.0;
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PROGRESS]);
+
+ dzl_binding_group_set_source (self->bindings,
+ ide_notification_stack_get_visible (self));
+
+ g_signal_emit (self, signals [CHANGED], 0);
+}
+
+static void
+ide_notification_stack_destroy (GtkWidget *widget)
+{
+ IdeNotificationStack *self = (IdeNotificationStack *)widget;
+
+ if (self->signals != NULL)
+ dzl_signal_group_set_target (self->signals, NULL);
+
+ if (self->bindings != NULL)
+ dzl_binding_group_set_source (self->bindings, NULL);
+
+ g_clear_object (&self->bindings);
+ g_clear_object (&self->signals);
+ g_clear_handle_id (&self->carousel_source, g_source_remove);
+
+ GTK_WIDGET_CLASS (ide_notification_stack_parent_class)->destroy (widget);
+}
+
+static void
+ide_notification_stack_class_init (IdeNotificationStackClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = ide_notification_stack_get_property;
+ object_class->set_property = ide_notification_stack_set_property;
+
+ widget_class->destroy = ide_notification_stack_destroy;
+
+ properties [PROP_PROGRESS] =
+ g_param_spec_double ("progress",
+ "Progress",
+ "The progress of the current item",
+ 0.0, 1.0, 0.0,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+
+ signals [CHANGED] =
+ g_signal_new ("changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ gtk_widget_class_set_css_name (widget_class, "notificationstack");
+}
+
+static void
+ide_notification_stack_init (IdeNotificationStack *self)
+{
+ self->signals = dzl_signal_group_new (G_TYPE_LIST_MODEL);
+
+ dzl_signal_group_connect_object (self->signals,
+ "items-changed",
+ G_CALLBACK (ide_notification_stack_items_changed_cb),
+ self,
+ G_CONNECT_SWAPPED);
+
+ self->bindings = dzl_binding_group_new ();
+
+ dzl_binding_group_bind (self->bindings, "progress", self, "progress",
+ G_BINDING_SYNC_CREATE);
+
+ gtk_stack_set_transition_duration (GTK_STACK (self), TRANSITION_DURATION);
+ gtk_stack_set_transition_type (GTK_STACK (self), GTK_STACK_TRANSITION_TYPE_SLIDE_UP_DOWN);
+
+ g_signal_connect (self,
+ "notify::visible-child",
+ G_CALLBACK (ide_notification_stack_notify_visible_child),
+ NULL);
+}
+
+void
+ide_notification_stack_bind_model (IdeNotificationStack *self,
+ GListModel *model)
+{
+ g_return_if_fail (IDE_IS_NOTIFICATION_STACK (self));
+ g_return_if_fail (!model || G_IS_LIST_MODEL (model));
+ g_return_if_fail (!model ||
+ g_type_is_a (g_list_model_get_item_type (model), IDE_TYPE_NOTIFICATION));
+
+ if (g_set_object (&self->model, model))
+ {
+ guint n_items = 0;
+
+ if (model != NULL)
+ n_items = g_list_model_get_n_items (model);
+
+ gtk_container_foreach (GTK_CONTAINER (self), (GtkCallback)gtk_widget_destroy, NULL);
+ dzl_signal_group_set_target (self->signals, model);
+
+ if (n_items > 0)
+ ide_notification_stack_items_changed_cb (self, 0, 0, n_items, model);
+ }
+}
+
+gboolean
+ide_notification_stack_get_can_move (IdeNotificationStack *self)
+{
+ g_return_val_if_fail (IDE_IS_NOTIFICATION_STACK (self), FALSE);
+
+ if (self->model != NULL)
+ return g_list_model_get_n_items (self->model) > 1;
+ else
+ return FALSE;
+}
+
+void
+ide_notification_stack_move_next (IdeNotificationStack *self)
+{
+ GtkWidget *child;
+ gint position;
+
+ g_return_if_fail (IDE_IS_NOTIFICATION_STACK (self));
+
+ if ((child = gtk_stack_get_visible_child (GTK_STACK (self))))
+ {
+ GList *children;
+
+ gtk_container_child_get (GTK_CONTAINER (self), child,
+ "position", &position,
+ NULL);
+ children = gtk_container_get_children (GTK_CONTAINER (self));
+ if (!(child = g_list_nth_data (children, position + 1)))
+ child = children->data;
+ g_list_free (children);
+
+ gtk_stack_set_transition_type (GTK_STACK (self), GTK_STACK_TRANSITION_TYPE_SLIDE_DOWN);
+ gtk_stack_set_visible_child (GTK_STACK (self), child);
+ gtk_stack_set_transition_type (GTK_STACK (self), GTK_STACK_TRANSITION_TYPE_SLIDE_UP_DOWN);
+
+ if (!self->in_carousel)
+ g_clear_handle_id (&self->carousel_source, g_source_remove);
+ }
+}
+
+void
+ide_notification_stack_move_previous (IdeNotificationStack *self)
+{
+ GtkWidget *child;
+ gint position;
+
+ g_return_if_fail (IDE_IS_NOTIFICATION_STACK (self));
+
+ if ((child = gtk_stack_get_visible_child (GTK_STACK (self))))
+ {
+ GList *children;
+
+ gtk_container_child_get (GTK_CONTAINER (self), child,
+ "position", &position,
+ NULL);
+ children = gtk_container_get_children (GTK_CONTAINER (self));
+ if (position == 0)
+ child = g_list_last (children)->data;
+ else
+ child = g_list_nth_data (children, position - 1);
+ g_list_free (children);
+
+ gtk_stack_set_transition_type (GTK_STACK (self), GTK_STACK_TRANSITION_TYPE_SLIDE_UP);
+ gtk_stack_set_visible_child (GTK_STACK (self), child);
+ gtk_stack_set_transition_type (GTK_STACK (self), GTK_STACK_TRANSITION_TYPE_SLIDE_UP_DOWN);
+
+ if (!self->in_carousel)
+ g_clear_handle_id (&self->carousel_source, g_source_remove);
+ }
+}
+
+/**
+ * ide_notification_stack_get_visible:
+ * @self: a #IdeNotificationStack
+ *
+ * Gets the visible notification in the stack.
+ *
+ * Returns: (transfer none) (nullable): an #IdeNotification or %NULL
+ *
+ * Since: 3.32
+ */
+IdeNotification *
+ide_notification_stack_get_visible (IdeNotificationStack *self)
+{
+ GtkWidget *child;
+
+ g_return_val_if_fail (IDE_IS_NOTIFICATION_STACK (self), NULL);
+
+ if ((child = gtk_stack_get_visible_child (GTK_STACK (self))))
+ {
+ if (IDE_IS_NOTIFICATION_VIEW (child))
+ return ide_notification_view_get_notification (IDE_NOTIFICATION_VIEW (child));
+ }
+
+ return NULL;
+}
+
+gdouble
+ide_notification_stack_get_progress (IdeNotificationStack *self)
+{
+ g_return_val_if_fail (IDE_IS_NOTIFICATION_STACK (self), 0.0);
+
+ return self->progress;
+}
+
+void
+ide_notification_stack_set_progress (IdeNotificationStack *self,
+ gdouble progress)
+{
+ g_return_if_fail (IDE_IS_NOTIFICATION_STACK (self));
+
+ progress = CLAMP (progress, 0.0, 1.0);
+
+ if (progress != self->progress)
+ {
+ self->progress = progress;
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PROGRESS]);
+ }
+}
+
+gboolean
+ide_notification_stack_is_empty (IdeNotificationStack *self)
+{
+ g_return_val_if_fail (IDE_IS_MAIN_THREAD (), FALSE);
+ g_return_val_if_fail (IDE_IS_NOTIFICATION_STACK (self), FALSE);
+
+ return self->model == NULL || g_list_model_get_n_items (self->model) == 0;
+}
diff --git a/src/libide/gui/ide-notification-view-private.h b/src/libide/gui/ide-notification-view-private.h
new file mode 100644
index 000000000..017a83fa5
--- /dev/null
+++ b/src/libide/gui/ide-notification-view-private.h
@@ -0,0 +1,37 @@
+/* ide-notification-view-private.h
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+#include <libide-core.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_NOTIFICATION_VIEW (ide_notification_view_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeNotificationView, ide_notification_view, IDE, NOTIFICATION_VIEW, GtkBin)
+
+GtkWidget *ide_notification_view_new (void);
+IdeNotification *ide_notification_view_get_notification (IdeNotificationView *self);
+void ide_notification_view_set_notification (IdeNotificationView *self,
+ IdeNotification *notification);
+
+G_END_DECLS
diff --git a/src/libide/gui/ide-notification-view.c b/src/libide/gui/ide-notification-view.c
new file mode 100644
index 000000000..8b160aee9
--- /dev/null
+++ b/src/libide/gui/ide-notification-view.c
@@ -0,0 +1,291 @@
+/* ide-notification-view.c
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-notification-view"
+
+#include "config.h"
+
+#include <dazzle.h>
+
+#include "ide-notification-view-private.h"
+
+struct _IdeNotificationView
+{
+ GtkBin parent_instance;
+
+ IdeNotification *notification;
+ DzlBindingGroup *bindings;
+
+ GtkLabel *label;
+ GtkBox *buttons;
+ GtkButton *default_button;
+ GtkImage *default_button_image;
+};
+
+G_DEFINE_TYPE (IdeNotificationView, ide_notification_view, GTK_TYPE_BIN)
+
+enum {
+ PROP_0,
+ PROP_NOTIFICATION,
+ N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+ide_notification_view_notify_icon (IdeNotificationView *self,
+ GParamSpec *pspec,
+ IdeNotification *notif)
+{
+ g_autoptr(GIcon) icon = NULL;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_NOTIFICATION_VIEW (self));
+ g_assert (IDE_IS_NOTIFICATION (notif));
+
+ icon = ide_notification_ref_icon (notif);
+ gtk_image_set_from_gicon (self->default_button_image, icon, GTK_ICON_SIZE_MENU);
+ gtk_widget_set_visible (GTK_WIDGET (self->default_button), icon != NULL);
+}
+
+static void
+connect_notification (IdeNotificationView *self,
+ IdeNotification *notification)
+{
+ g_autofree gchar *action_name = NULL;
+ g_autoptr(GVariant) target_value = NULL;
+ g_autoptr(GIcon) icon = NULL;
+ guint n_buttons;
+
+ g_assert (IDE_IS_NOTIFICATION_VIEW (self));
+ g_assert (!notification || IDE_IS_NOTIFICATION (notification));
+
+ gtk_container_foreach (GTK_CONTAINER (self->buttons), (GtkCallback)gtk_widget_destroy, NULL);
+
+ if (notification == NULL)
+ {
+ gtk_widget_hide (GTK_WIDGET (self->label));
+ gtk_widget_hide (GTK_WIDGET (self->default_button));
+ gtk_widget_hide (GTK_WIDGET (self->buttons));
+ return;
+ }
+
+ g_signal_connect_object (notification,
+ "notify::icon",
+ G_CALLBACK (ide_notification_view_notify_icon),
+ self,
+ G_CONNECT_SWAPPED);
+ ide_notification_view_notify_icon (self, NULL, notification);
+
+ /*
+ * Setup the default action button (which is shown right after the label
+ * containing notification title).
+ */
+
+ if (ide_notification_get_default_action (notification, &action_name, &target_value))
+ {
+ gtk_actionable_set_action_name (GTK_ACTIONABLE (self->default_button), action_name);
+ gtk_actionable_set_action_target_value (GTK_ACTIONABLE (self->default_button), target_value);
+ }
+
+ /*
+ * Now add all of the buttons requested by the notification.
+ */
+
+ ide_object_lock (IDE_OBJECT (notification));
+
+ n_buttons = ide_notification_get_n_buttons (notification);
+
+ for (guint i = 0; i < n_buttons; i++)
+ {
+ g_autofree gchar *action = NULL;
+ g_autofree gchar *label = NULL;
+ g_autoptr(GIcon) button_icon = NULL;
+ g_autoptr(GVariant) target = NULL;
+
+ if (ide_notification_get_button (notification, i, &label, &button_icon, &action, &target) &&
+ button_icon != NULL &&
+ action_name != NULL)
+ {
+ GtkButton *button;
+
+ button = g_object_new (GTK_TYPE_BUTTON,
+ "child", g_object_new (GTK_TYPE_IMAGE,
+ "gicon", button_icon,
+ "visible", TRUE,
+ NULL),
+ "action-name", action,
+ "action-target", target,
+ "has-tooltip", TRUE,
+ "tooltip-text", label,
+ "visible", TRUE,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (self->buttons), GTK_WIDGET (button));
+ }
+ }
+
+ ide_object_unlock (IDE_OBJECT (notification));
+}
+
+static void
+ide_notification_view_finalize (GObject *object)
+{
+ IdeNotificationView *self = (IdeNotificationView *)object;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+
+ g_clear_object (&self->bindings);
+ g_clear_object (&self->notification);
+
+ G_OBJECT_CLASS (ide_notification_view_parent_class)->finalize (object);
+}
+
+static void
+ide_notification_view_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdeNotificationView *self = IDE_NOTIFICATION_VIEW (object);
+
+ switch (prop_id)
+ {
+ case PROP_NOTIFICATION:
+ g_value_set_object (value, ide_notification_view_get_notification (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_notification_view_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdeNotificationView *self = IDE_NOTIFICATION_VIEW (object);
+
+ switch (prop_id)
+ {
+ case PROP_NOTIFICATION:
+ ide_notification_view_set_notification (self, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_notification_view_class_init (IdeNotificationViewClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = ide_notification_view_finalize;
+ object_class->get_property = ide_notification_view_get_property;
+ object_class->set_property = ide_notification_view_set_property;
+
+ /**
+ * IdeNotificationView:notification:
+ *
+ * The "notification" property is the #IdeNotification to be displayed.
+ *
+ * Since: 3.32
+ */
+ properties [PROP_NOTIFICATION] =
+ g_param_spec_object ("notification",
+ "Notification",
+ "The IdeNotification to be viewed",
+ IDE_TYPE_NOTIFICATION,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+
+ gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/libide-gui/ui/ide-notification-view.ui");
+ gtk_widget_class_set_css_name (widget_class, "notification");
+ gtk_widget_class_bind_template_child (widget_class, IdeNotificationView, label);
+ gtk_widget_class_bind_template_child (widget_class, IdeNotificationView, buttons);
+ gtk_widget_class_bind_template_child (widget_class, IdeNotificationView, default_button);
+ gtk_widget_class_bind_template_child (widget_class, IdeNotificationView, default_button_image);
+}
+
+static void
+ide_notification_view_init (IdeNotificationView *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ self->bindings = dzl_binding_group_new ();
+
+ dzl_binding_group_bind (self->bindings, "title", self->label, "label", G_BINDING_SYNC_CREATE);
+}
+
+/**
+ * ide_notification_view_new:
+ *
+ * Create a new #IdeNotificationView to visualize a notification within
+ * the #IdeOmniBar.
+ *
+ * Returns: (transfer full): a newly created #IdeNotificationView
+ *
+ * Since: 3.32
+ */
+GtkWidget *
+ide_notification_view_new (void)
+{
+ return g_object_new (IDE_TYPE_NOTIFICATION_VIEW, NULL);
+}
+
+/**
+ * ide_notification_view_get_notification:
+ * @self: an #IdeNotificationView
+ *
+ * Gets the #IdeNotification that is being viewed.
+ *
+ * Returns: (transfer none) (nullable): an #IdeNotification or %NULL
+ *
+ * Since: 3.32
+ */
+IdeNotification *
+ide_notification_view_get_notification (IdeNotificationView *self)
+{
+ g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
+ g_return_val_if_fail (IDE_IS_NOTIFICATION_VIEW (self), NULL);
+
+ return self->notification;
+}
+
+void
+ide_notification_view_set_notification (IdeNotificationView *self,
+ IdeNotification *notification)
+{
+ g_return_if_fail (IDE_IS_MAIN_THREAD ());
+ g_return_if_fail (IDE_IS_NOTIFICATION_VIEW (self));
+ g_return_if_fail (!notification || IDE_IS_NOTIFICATION (notification));
+
+ if (g_set_object (&self->notification, notification))
+ {
+ dzl_binding_group_set_source (self->bindings, notification);
+ connect_notification (self, notification);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_NOTIFICATION]);
+ }
+}
diff --git a/src/libide/gui/ide-notification-view.ui b/src/libide/gui/ide-notification-view.ui
new file mode 100644
index 000000000..bc7b177cf
--- /dev/null
+++ b/src/libide/gui/ide-notification-view.ui
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.0 -->
+<interface>
+ <requires lib="gtk+" version="3.24"/>
+ <template class="IdeNotificationView" parent="GtkBin">
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label">
+ <property name="ellipsize">end</property>
+ <property name="margin-start">6</property>
+ <property name="visible">True</property>
+ <property name="width_chars">5</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="default_button">
+ <property name="can_focus">True</property>
+ <property name="focus_on_click">False</property>
+ <property name="receives_default">True</property>
+ <child>
+ <object class="GtkImage" id="default_button_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="stock">gtk-missing-image</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="buttons">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">end</property>
+ <property name="hexpand">True</property>
+ <property name="spacing">6</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
+
diff --git a/src/libide/gui/ide-notifications-button-popover-private.h
b/src/libide/gui/ide-notifications-button-popover-private.h
new file mode 100644
index 000000000..180506cfc
--- /dev/null
+++ b/src/libide/gui/ide-notifications-button-popover-private.h
@@ -0,0 +1,31 @@
+/* ide-notifications-button-popover-private.h
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_NOTIFICATIONS_BUTTON_POPOVER (ide_notifications_button_popover_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeNotificationsButtonPopover, ide_notifications_button_popover, IDE,
NOTIFICATIONS_BUTTON_POPOVER, GtkPopover)
+
+G_END_DECLS
diff --git a/src/libide/gui/ide-notifications-button-popover.c
b/src/libide/gui/ide-notifications-button-popover.c
new file mode 100644
index 000000000..b89e45be9
--- /dev/null
+++ b/src/libide/gui/ide-notifications-button-popover.c
@@ -0,0 +1,51 @@
+/* ide-notifications-button-popover.c
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-notifications-button-popover"
+
+#include "config.h"
+
+#include "ide-notifications-button-popover-private.h"
+
+struct _IdeNotificationsButtonPopover
+{
+ GtkPopover parent_instance;
+};
+
+G_DEFINE_TYPE (IdeNotificationsButtonPopover, ide_notifications_button_popover, GTK_TYPE_POPOVER)
+
+static GtkSizeRequestMode
+ide_notifications_button_popover_get_request_mode (GtkWidget *widget)
+{
+ return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
+}
+
+static void
+ide_notifications_button_popover_class_init (IdeNotificationsButtonPopoverClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ widget_class->get_request_mode = ide_notifications_button_popover_get_request_mode;
+}
+
+static void
+ide_notifications_button_popover_init (IdeNotificationsButtonPopover *self)
+{
+}
diff --git a/src/libide/gui/ide-notifications-button.c b/src/libide/gui/ide-notifications-button.c
new file mode 100644
index 000000000..a9e47e41e
--- /dev/null
+++ b/src/libide/gui/ide-notifications-button.c
@@ -0,0 +1,217 @@
+/* ide-notifications-button.c
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-notifications-button"
+
+#include "config.h"
+
+#include "ide-notifications-button.h"
+#include "ide-gui-global.h"
+#include "ide-gui-private.h"
+
+/**
+ * SECTION:ide-notifications-button:
+ * @title: IdeNotificationsButton
+ * @short_description: a popover menu button containing progress notifications
+ *
+ * The #IdeNotificationsButton shows ongoing notifications that have progress.
+ * The individual notifications are displayed in a popover with appropriate
+ * progress show for each.
+ *
+ * The button itself will show a "combined" progress of all the active
+ * notifications.
+ *
+ * Since: 3.32
+ */
+
+struct _IdeNotificationsButton
+{
+ DzlProgressMenuButton parent_instance;
+
+ GListModel *model;
+ DzlListModelFilter *filter;
+
+ /* Template widgets */
+ GtkPopover *popover;
+ GtkListBox *list_box;
+};
+
+G_DEFINE_TYPE (IdeNotificationsButton, ide_notifications_button, DZL_TYPE_PROGRESS_MENU_BUTTON)
+
+static GtkWidget *
+create_notification_row (gpointer item,
+ gpointer user_data)
+{
+ IdeNotification *notif = item;
+ gboolean has_default;
+
+ g_assert (IDE_IS_NOTIFICATION (notif));
+ g_assert (IDE_IS_NOTIFICATIONS_BUTTON (user_data));
+
+ has_default = ide_notification_get_default_action (notif, NULL, NULL);
+
+ return g_object_new (IDE_TYPE_NOTIFICATION_LIST_BOX_ROW,
+ "activatable", has_default,
+ "compact", TRUE,
+ "notification", item,
+ "visible", TRUE,
+ NULL);
+}
+
+static gboolean
+filter_by_has_progress (GObject *object,
+ gpointer user_data)
+{
+ IdeNotification *notif = (IdeNotification *)object;
+
+ g_assert (IDE_IS_NOTIFICATION (notif));
+ g_assert (user_data == NULL);
+
+ return ide_notification_get_has_progress (notif);
+}
+
+static void
+ide_notifications_button_bind_model (IdeNotificationsButton *self,
+ GListModel *model)
+{
+ g_assert (IDE_IS_NOTIFICATIONS_BUTTON (self));
+ g_assert (G_IS_LIST_MODEL (model));
+
+ if (g_set_object (&self->model, model))
+ {
+ g_clear_object (&self->filter);
+
+ self->filter = dzl_list_model_filter_new (model);
+ dzl_list_model_filter_set_filter_func (self->filter,
+ filter_by_has_progress,
+ NULL, NULL);
+
+ gtk_list_box_bind_model (self->list_box,
+ G_LIST_MODEL (self->filter),
+ create_notification_row,
+ self, NULL);
+ }
+}
+
+static void
+ide_notifications_button_context_set_cb (GtkWidget *widget,
+ IdeContext *context)
+{
+ IdeNotificationsButton *self = (IdeNotificationsButton *)widget;
+ g_autoptr(IdeNotifications) notifications = NULL;
+
+ g_assert (IDE_IS_NOTIFICATIONS_BUTTON (self));
+ g_assert (IDE_IS_CONTEXT (context));
+
+ notifications = ide_object_get_child_typed (IDE_OBJECT (context), IDE_TYPE_NOTIFICATIONS);
+ ide_notifications_button_bind_model (self, G_LIST_MODEL (notifications));
+
+ g_object_bind_property (notifications, "progress", self, "progress",
+ G_BINDING_SYNC_CREATE);
+ g_object_bind_property (notifications, "has-progress", self, "visible",
+ G_BINDING_SYNC_CREATE);
+ g_object_bind_property (notifications, "progress-is-imprecise", self, "show-progress",
+ G_BINDING_INVERT_BOOLEAN | G_BINDING_SYNC_CREATE);
+}
+
+static void
+ide_notifications_button_row_activated (IdeNotificationsButton *self,
+ IdeNotificationListBoxRow *row,
+ GtkListBox *list_box)
+{
+ g_autofree gchar *default_action = NULL;
+ g_autoptr(GVariant) default_target = NULL;
+ IdeNotification *notif;
+
+ g_assert (IDE_IS_NOTIFICATIONS_BUTTON (self));
+ g_assert (IDE_IS_NOTIFICATION_LIST_BOX_ROW (row));
+ g_assert (GTK_IS_LIST_BOX (list_box));
+
+ notif = ide_notification_list_box_row_get_notification (row);
+
+ if (ide_notification_get_default_action (notif, &default_action, &default_target))
+ {
+ gchar *name = strchr (default_action, '.');
+ gchar *group = default_action;
+
+ if (name != NULL)
+ {
+ *name = '\0';
+ name++;
+ }
+ else
+ {
+ group = NULL;
+ name = default_action;
+ }
+
+ dzl_gtk_widget_action (GTK_WIDGET (list_box), group, name, default_target);
+ }
+}
+
+static void
+ide_notifications_button_destroy (GtkWidget *widget)
+{
+ IdeNotificationsButton *self = (IdeNotificationsButton *)widget;
+
+ g_assert (IDE_IS_NOTIFICATIONS_BUTTON (self));
+
+ g_clear_object (&self->filter);
+ g_clear_object (&self->model);
+
+ GTK_WIDGET_CLASS (ide_notifications_button_parent_class)->destroy (widget);
+}
+
+static void
+ide_notifications_button_class_init (IdeNotificationsButtonClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ widget_class->destroy = ide_notifications_button_destroy;
+
+ gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/libide-gui/ui/ide-notifications-button.ui");
+ gtk_widget_class_bind_template_child (widget_class, IdeNotificationsButton, list_box);
+ gtk_widget_class_bind_template_child (widget_class, IdeNotificationsButton, popover);
+ gtk_widget_class_bind_template_callback (widget_class, ide_notifications_button_row_activated);
+}
+
+static void
+ide_notifications_button_init (IdeNotificationsButton *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ ide_widget_set_context_handler (GTK_WIDGET (self),
+ ide_notifications_button_context_set_cb);
+}
+
+/**
+ * ide_notifications_button_new:
+ *
+ * Create a new #IdeNotificationsButton.
+ *
+ * Returns: (transfer full): a newly created #IdeNotificationsButton
+ *
+ * Since: 3.32
+ */
+GtkWidget *
+ide_notifications_button_new (void)
+{
+ return g_object_new (IDE_TYPE_NOTIFICATIONS_BUTTON, NULL);
+}
diff --git a/src/libide/gui/ide-notifications-button.h b/src/libide/gui/ide-notifications-button.h
new file mode 100644
index 000000000..703d63f4b
--- /dev/null
+++ b/src/libide/gui/ide-notifications-button.h
@@ -0,0 +1,40 @@
+/* ide-notifications-button.h
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#if !defined (IDE_GUI_INSIDE) && !defined (IDE_GUI_COMPILATION)
+# error "Only <libide-gui.h> can be included directly."
+#endif
+
+#include <libide-core.h>
+#include <dazzle.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_NOTIFICATIONS_BUTTON (ide_notifications_button_get_type())
+
+IDE_AVAILABLE_IN_3_32
+G_DECLARE_FINAL_TYPE (IdeNotificationsButton, ide_notifications_button, IDE, NOTIFICATIONS_BUTTON,
DzlProgressMenuButton)
+
+IDE_AVAILABLE_IN_3_32
+GtkWidget *ide_notifications_button_new (void);
+
+G_END_DECLS
diff --git a/src/libide/gui/ide-notifications-button.ui b/src/libide/gui/ide-notifications-button.ui
new file mode 100644
index 000000000..ebf209a45
--- /dev/null
+++ b/src/libide/gui/ide-notifications-button.ui
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="IdeNotificationsButton" parent="DzlProgressMenuButton">
+ <property name="show-progress">false</property>
+ <property name="popover">popover</property>
+ </template>
+ <object class="GtkPopover" id="popover">
+ <style>
+ <class name="notificationsbutton"/>
+ </style>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">true</property>
+ <property name="max-content-width">400</property>
+ <property name="min-content-width">400</property>
+ <property name="hscrollbar-policy">never</property>
+ <property name="propagate-natural-width">false</property>
+ <property name="propagate-natural-height">true</property>
+ <child>
+ <object class="GtkListBox" id="list_box">
+ <signal name="row-activated" handler="ide_notifications_button_row_activated" swapped="true"
object="IdeNotificationsButton"/>
+ <property name="selection-mode">none</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
+
+
+
diff --git a/src/libide/gui/ide-omni-bar-addin.c b/src/libide/gui/ide-omni-bar-addin.c
new file mode 100644
index 000000000..49d6a3b30
--- /dev/null
+++ b/src/libide/gui/ide-omni-bar-addin.c
@@ -0,0 +1,89 @@
+/* ide-omni-bar-addin.c
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-omni-bar-addin"
+
+#include "config.h"
+
+#include "ide-omni-bar-addin.h"
+
+/**
+ * SECTION:ide-omni-bar-addin
+ * @title: IdeOmniBarAddin
+ * @short_description: addins to extend the #IdeOmniBar
+ *
+ * The #IdeOmniBarAddin allows plugins to extend how the #IdeOmniBar
+ * works. They can add additional components such as buttons, or more
+ * information to the popover.
+ *
+ * See #IdeOmniBar for information about what you can alter.
+ *
+ * Since: 3.32
+ */
+
+G_DEFINE_INTERFACE (IdeOmniBarAddin, ide_omni_bar_addin, G_TYPE_OBJECT)
+
+static void
+ide_omni_bar_addin_default_init (IdeOmniBarAddinInterface *iface)
+{
+}
+
+/**
+ * ide_omni_bar_addin_load:
+ * @self: an #IdeOmniBarAddin
+ * @omni_bar: an #IdeOmniBar
+ *
+ * Requests that the #IdeOmniBarAddin initialize, possibly modifying
+ * @omni_bar as necessary.
+ *
+ * Since: 3.32
+ */
+void
+ide_omni_bar_addin_load (IdeOmniBarAddin *self,
+ IdeOmniBar *omni_bar)
+{
+ g_return_if_fail (IDE_IS_OMNI_BAR_ADDIN (self));
+ g_return_if_fail (IDE_IS_OMNI_BAR (omni_bar));
+
+ if (IDE_OMNI_BAR_ADDIN_GET_IFACE (self)->load)
+ IDE_OMNI_BAR_ADDIN_GET_IFACE (self)->load (self, omni_bar);
+}
+
+/**
+ * ide_omni_bar_addin_unload:
+ * @self: an #IdeOmniBarAddin
+ * @omni_bar: an #IdeOmniBar
+ *
+ * Requests that the #IdeOmniBarAddin shutdown, possibly modifying
+ * @omni_bar as necessary to return it to the original state before
+ * the addin was loaded.
+ *
+ * Since: 3.32
+ */
+void
+ide_omni_bar_addin_unload (IdeOmniBarAddin *self,
+ IdeOmniBar *omni_bar)
+{
+ g_return_if_fail (IDE_IS_OMNI_BAR_ADDIN (self));
+ g_return_if_fail (IDE_IS_OMNI_BAR (omni_bar));
+
+ if (IDE_OMNI_BAR_ADDIN_GET_IFACE (self)->unload)
+ IDE_OMNI_BAR_ADDIN_GET_IFACE (self)->unload (self, omni_bar);
+}
diff --git a/src/libide/gui/ide-omni-bar-addin.h b/src/libide/gui/ide-omni-bar-addin.h
new file mode 100644
index 000000000..0b2290d39
--- /dev/null
+++ b/src/libide/gui/ide-omni-bar-addin.h
@@ -0,0 +1,55 @@
+/* ide-omni-bar-addin.h
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#if !defined (IDE_GUI_INSIDE) && !defined (IDE_GUI_COMPILATION)
+# error "Only <libide-gui.h> can be included directly."
+#endif
+
+#include <libide-core.h>
+
+#include "ide-omni-bar.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_OMNI_BAR_ADDIN (ide_omni_bar_addin_get_type ())
+
+IDE_AVAILABLE_IN_3_32
+G_DECLARE_INTERFACE (IdeOmniBarAddin, ide_omni_bar_addin, IDE, OMNI_BAR_ADDIN, GObject)
+
+struct _IdeOmniBarAddinInterface
+{
+ GTypeInterface parent;
+
+ void (*load) (IdeOmniBarAddin *self,
+ IdeOmniBar *omni_bar);
+ void (*unload) (IdeOmniBarAddin *self,
+ IdeOmniBar *omni_bar);
+};
+
+IDE_AVAILABLE_IN_3_32
+void ide_omni_bar_addin_load (IdeOmniBarAddin *self,
+ IdeOmniBar *omni_bar);
+IDE_AVAILABLE_IN_3_32
+void ide_omni_bar_addin_unload (IdeOmniBarAddin *self,
+ IdeOmniBar *omni_bar);
+
+G_END_DECLS
diff --git a/src/libide/gui/ide-omni-bar.c b/src/libide/gui/ide-omni-bar.c
new file mode 100644
index 000000000..fbf8c6399
--- /dev/null
+++ b/src/libide/gui/ide-omni-bar.c
@@ -0,0 +1,619 @@
+/* ide-omni-bar.c
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-omni-bar"
+
+#include "config.h"
+
+#include <libpeas/peas.h>
+#include <dazzle.h>
+
+#include "ide-gui-global.h"
+#include "ide-gui-private.h"
+#include "ide-notification-list-box-row-private.h"
+#include "ide-notification-stack-private.h"
+#include "ide-omni-bar-addin.h"
+#include "ide-omni-bar.h"
+
+struct _IdeOmniBar
+{
+ GtkEventBox parent_instance;
+
+ PeasExtensionSet *addins;
+ GtkGesture *gesture;
+ GtkEventController *motion;
+
+ GtkStack *top_stack;
+ GtkPopover *popover;
+ DzlEntryBox *entry_box;
+ IdeNotificationStack *notification_stack;
+ GtkListBox *notifications_list_box;
+ DzlPriorityBox *inner_box;
+ DzlPriorityBox *outer_box;
+ GtkProgressBar *progress;
+ GtkWidget *placeholder;
+ DzlPriorityBox *sections_box;
+};
+
+static void ide_omni_bar_move_next (IdeOmniBar *self,
+ GVariant *param);
+static void ide_omni_bar_move_previous (IdeOmniBar *self,
+ GVariant *param);
+static void buildable_iface_init (GtkBuildableIface *iface);
+
+DZL_DEFINE_ACTION_GROUP (IdeOmniBar, ide_omni_bar, {
+ { "move-next", ide_omni_bar_move_next },
+ { "move-previous", ide_omni_bar_move_previous },
+})
+
+G_DEFINE_TYPE_WITH_CODE (IdeOmniBar, ide_omni_bar, GTK_TYPE_EVENT_BOX,
+ G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, ide_omni_bar_init_action_group)
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, buildable_iface_init))
+
+static GtkBuildableIface *parent_buildable_iface;
+
+static void
+ide_omni_bar_popover_closed_cb (IdeOmniBar *self,
+ GtkPopover *popover)
+{
+ GtkStyleContext *style_context;
+ GtkStateFlags state_flags;
+
+ g_assert (IDE_IS_OMNI_BAR (self));
+ g_assert (GTK_IS_POPOVER (popover));
+
+ style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
+ state_flags = gtk_style_context_get_state (style_context);
+
+ state_flags &= ~GTK_STATE_FLAG_ACTIVE;
+ state_flags &= ~GTK_STATE_FLAG_PRELIGHT;
+
+ gtk_style_context_set_state (style_context, state_flags);
+}
+
+static void
+multipress_pressed_cb (IdeOmniBar *self,
+ guint n_press,
+ gdouble x,
+ gdouble y,
+ GtkGestureMultiPress *gesture)
+{
+ GtkStyleContext *style_context;
+ GtkStateFlags state_flags;
+
+ g_assert (IDE_IS_OMNI_BAR (self));
+ g_assert (GTK_IS_GESTURE_MULTI_PRESS (gesture));
+
+ gtk_popover_popup (self->popover);
+
+ style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
+ state_flags = gtk_style_context_get_state (style_context);
+ gtk_style_context_set_state (style_context, state_flags | GTK_STATE_FLAG_ACTIVE);
+
+ gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
+}
+
+static void
+ide_omni_bar_notification_stack_changed_cb (IdeOmniBar *self,
+ IdeNotificationStack *stack)
+{
+ IdeNotification *notif;
+ gboolean enabled;
+
+ g_assert (IDE_IS_OMNI_BAR (self));
+ g_assert (IDE_IS_NOTIFICATION_STACK (stack));
+
+ enabled = ide_notification_stack_get_can_move (stack);
+
+ ide_omni_bar_set_action_enabled (self, "move-previous", enabled);
+ ide_omni_bar_set_action_enabled (self, "move-next", enabled);
+
+ _ide_gtk_progress_bar_stop_pulsing (self->progress);
+ gtk_widget_hide (GTK_WIDGET (self->progress));
+
+ if ((notif = ide_notification_stack_get_visible (stack)))
+ {
+ if (ide_notification_get_has_progress (notif))
+ {
+ if (ide_notification_get_progress_is_imprecise (notif))
+ _ide_gtk_progress_bar_start_pulsing (self->progress);
+ gtk_widget_show (GTK_WIDGET (self->progress));
+ }
+ }
+
+ if (ide_notification_stack_is_empty (stack))
+ gtk_stack_set_visible_child_name (self->top_stack, "placeholder");
+ else
+ gtk_stack_set_visible_child_name (self->top_stack, "notifications");
+}
+
+static void
+ide_omni_bar_extension_added_cb (PeasExtensionSet *set,
+ PeasPluginInfo *plugin_info,
+ PeasExtension *exten,
+ gpointer user_data)
+{
+ IdeOmniBarAddin *addin = (IdeOmniBarAddin *)exten;
+ IdeOmniBar *self = user_data;
+
+ g_assert (PEAS_IS_EXTENSION_SET (set));
+ g_assert (plugin_info != NULL);
+ g_assert (IDE_IS_OMNI_BAR_ADDIN (addin));
+ g_assert (IDE_IS_OMNI_BAR (self));
+
+ ide_omni_bar_addin_load (addin, self);
+}
+
+static void
+ide_omni_bar_extension_removed_cb (PeasExtensionSet *set,
+ PeasPluginInfo *plugin_info,
+ PeasExtension *exten,
+ gpointer user_data)
+{
+ IdeOmniBarAddin *addin = (IdeOmniBarAddin *)exten;
+ IdeOmniBar *self = user_data;
+
+ g_assert (PEAS_IS_EXTENSION_SET (set));
+ g_assert (plugin_info != NULL);
+ g_assert (IDE_IS_OMNI_BAR_ADDIN (addin));
+ g_assert (IDE_IS_OMNI_BAR (self));
+
+ ide_omni_bar_addin_unload (addin, self);
+}
+
+static GtkWidget *
+create_notification_row (gpointer item,
+ gpointer user_data)
+{
+ IdeNotification *notif = item;
+ gboolean has_default;
+
+ g_assert (IDE_IS_NOTIFICATION (notif));
+
+ has_default = ide_notification_get_default_action (notif, NULL, NULL);
+
+ return g_object_new (IDE_TYPE_NOTIFICATION_LIST_BOX_ROW,
+ "activatable", has_default,
+ "notification", notif,
+ "visible", TRUE,
+ NULL);
+}
+
+static gboolean
+filter_for_popover (GObject *object,
+ gpointer user_data)
+{
+ IdeNotification *notif = (IdeNotification *)object;
+
+ g_assert (IDE_IS_NOTIFICATION (notif));
+ g_assert (user_data == NULL);
+
+ return !ide_notification_get_has_progress (notif) &&
+ ide_notification_get_urgent (notif);
+}
+
+static void
+ide_omni_bar_context_set_cb (GtkWidget *widget,
+ IdeContext *context)
+{
+ IdeOmniBar *self = (IdeOmniBar *)widget;
+ g_autoptr(IdeObject) notifications = NULL;
+ g_autoptr(DzlListModelFilter) filter = NULL;
+
+ g_assert (IDE_IS_OMNI_BAR (self));
+ g_assert (IDE_IS_CONTEXT (context));
+ g_assert (self->addins == NULL);
+
+ notifications = ide_object_get_child_typed (IDE_OBJECT (context), IDE_TYPE_NOTIFICATIONS);
+ ide_notification_stack_bind_model (self->notification_stack, G_LIST_MODEL (notifications));
+
+ filter = dzl_list_model_filter_new (G_LIST_MODEL (notifications));
+ dzl_list_model_filter_set_filter_func (filter, filter_for_popover, NULL, NULL);
+ gtk_list_box_bind_model (self->notifications_list_box,
+ G_LIST_MODEL (filter),
+ create_notification_row,
+ NULL, NULL);
+
+ self->addins = peas_extension_set_new (peas_engine_get_default (),
+ IDE_TYPE_OMNI_BAR_ADDIN,
+ NULL);
+
+ g_signal_connect (self->addins,
+ "extension-added",
+ G_CALLBACK (ide_omni_bar_extension_added_cb),
+ self);
+ g_signal_connect (self->addins,
+ "extension-removed",
+ G_CALLBACK (ide_omni_bar_extension_removed_cb),
+ self);
+
+ peas_extension_set_foreach (self->addins,
+ ide_omni_bar_extension_added_cb,
+ self);
+}
+
+static void
+ide_omni_bar_motion_enter_cb (IdeOmniBar *self,
+ gdouble x,
+ gdouble y,
+ GtkEventControllerMotion *motion)
+{
+ GtkStyleContext *style_context;
+ GtkStateFlags state_flags;
+
+ g_assert (IDE_IS_OMNI_BAR (self));
+ g_assert (GTK_IS_EVENT_CONTROLLER_MOTION (motion));
+
+ style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
+ state_flags = gtk_style_context_get_state (style_context);
+
+ if ((state_flags & GTK_STATE_FLAG_PRELIGHT) == 0)
+ gtk_style_context_set_state (style_context, state_flags | GTK_STATE_FLAG_PRELIGHT);
+}
+
+static void
+ide_omni_bar_motion_leave_cb (IdeOmniBar *self,
+ GtkEventControllerMotion *motion)
+{
+ GtkStyleContext *style_context;
+ GtkStateFlags state_flags;
+
+ g_assert (IDE_IS_OMNI_BAR (self));
+ g_assert (GTK_IS_EVENT_CONTROLLER_MOTION (motion));
+
+ style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
+ state_flags = gtk_style_context_get_state (style_context);
+
+ if (state_flags & GTK_STATE_FLAG_PRELIGHT)
+ gtk_style_context_set_state (style_context, state_flags & ~GTK_STATE_FLAG_PRELIGHT);
+}
+
+static void
+ide_omni_bar_motion_cb (IdeOmniBar *self,
+ gdouble x,
+ gdouble y,
+ GtkEventControllerMotion *motion)
+{
+ g_assert (IDE_IS_OMNI_BAR (self));
+ g_assert (GTK_IS_EVENT_CONTROLLER_MOTION (motion));
+
+ /*
+ * Because of how crossing-events work with Gtk 3, we don't get reliable
+ * crossing events for the motion controller. So every motion (which we do
+ * seem to get semi-reliably), just re-run the enter-notify path to ensure
+ * we get proper state set.
+ */
+
+ ide_omni_bar_motion_enter_cb (self, x, y, motion);
+}
+
+static gboolean
+ide_omni_bar_query_tooltip (GtkWidget *widget,
+ gint x,
+ gint y,
+ gboolean keyboard_mode,
+ GtkTooltip *tooltip)
+{
+ IdeOmniBar *self = (IdeOmniBar *)widget;
+ IdeNotification *notif;
+
+ g_assert (IDE_IS_OMNI_BAR (self));
+ g_assert (GTK_IS_TOOLTIP (tooltip));
+
+ if ((notif = ide_notification_stack_get_visible (self->notification_stack)))
+ {
+ g_autofree gchar *body = ide_notification_dup_body (notif);
+
+ if (body != NULL)
+ {
+ gtk_tooltip_set_text (tooltip, body);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+ide_omni_bar_notification_row_activated (IdeOmniBar *self,
+ IdeNotificationListBoxRow *row,
+ GtkListBox *list_box)
+{
+ g_autofree gchar *default_action = NULL;
+ g_autoptr(GVariant) default_target = NULL;
+ IdeNotification *notif;
+
+ g_assert (IDE_IS_OMNI_BAR (self));
+ g_assert (IDE_IS_NOTIFICATION_LIST_BOX_ROW (row));
+ g_assert (GTK_IS_LIST_BOX (list_box));
+
+ notif = ide_notification_list_box_row_get_notification (row);
+
+ if (ide_notification_get_default_action (notif, &default_action, &default_target))
+ {
+ gchar *name = strchr (default_action, '.');
+ gchar *group = default_action;
+
+ if (name != NULL)
+ {
+ *name = '\0';
+ name++;
+ }
+ else
+ {
+ group = NULL;
+ name = default_action;
+ }
+
+ dzl_gtk_widget_action (GTK_WIDGET (list_box), group, name, default_target);
+ }
+}
+
+static void
+ide_omni_bar_destroy (GtkWidget *widget)
+{
+ IdeOmniBar *self = (IdeOmniBar *)widget;
+
+ g_assert (IDE_IS_OMNI_BAR (self));
+
+ if (self->progress != NULL)
+ _ide_gtk_progress_bar_stop_pulsing (self->progress);
+
+ g_clear_object (&self->addins);
+ g_clear_object (&self->gesture);
+ g_clear_object (&self->motion);
+
+ GTK_WIDGET_CLASS (ide_omni_bar_parent_class)->destroy (widget);
+}
+
+static void
+ide_omni_bar_class_init (IdeOmniBarClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ widget_class->destroy = ide_omni_bar_destroy;
+ widget_class->query_tooltip = ide_omni_bar_query_tooltip;
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/libide-gui/ui/ide-omni-bar.ui");
+ gtk_widget_class_set_css_name (widget_class, "omnibar");
+ gtk_widget_class_bind_template_child (widget_class, IdeOmniBar, entry_box);
+ gtk_widget_class_bind_template_child (widget_class, IdeOmniBar, inner_box);
+ gtk_widget_class_bind_template_child (widget_class, IdeOmniBar, notification_stack);
+ gtk_widget_class_bind_template_child (widget_class, IdeOmniBar, notifications_list_box);
+ gtk_widget_class_bind_template_child (widget_class, IdeOmniBar, outer_box);
+ gtk_widget_class_bind_template_child (widget_class, IdeOmniBar, popover);
+ gtk_widget_class_bind_template_child (widget_class, IdeOmniBar, progress);
+ gtk_widget_class_bind_template_child (widget_class, IdeOmniBar, sections_box);
+ gtk_widget_class_bind_template_child (widget_class, IdeOmniBar, top_stack);
+ gtk_widget_class_bind_template_callback (widget_class, ide_omni_bar_notification_row_activated);
+
+ g_type_ensure (DZL_TYPE_ENTRY_BOX);
+ g_type_ensure (IDE_TYPE_NOTIFICATION_STACK);
+}
+
+static void
+ide_omni_bar_init (IdeOmniBar *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ gtk_widget_set_has_tooltip (GTK_WIDGET (self), TRUE);
+
+ gtk_widget_add_events (GTK_WIDGET (self),
+ (GDK_POINTER_MOTION_MASK |
+ GDK_ENTER_NOTIFY_MASK |
+ GDK_LEAVE_NOTIFY_MASK));
+
+ self->motion = gtk_event_controller_motion_new (GTK_WIDGET (self));
+ gtk_event_controller_set_propagation_phase (self->motion, GTK_PHASE_CAPTURE);
+
+ g_signal_connect_swapped (self->motion,
+ "enter",
+ G_CALLBACK (ide_omni_bar_motion_enter_cb),
+ self);
+
+ g_signal_connect_swapped (self->motion,
+ "motion",
+ G_CALLBACK (ide_omni_bar_motion_cb),
+ self);
+
+ g_signal_connect_swapped (self->motion,
+ "leave",
+ G_CALLBACK (ide_omni_bar_motion_leave_cb),
+ self);
+
+ self->gesture = gtk_gesture_multi_press_new (GTK_WIDGET (self));
+
+ g_signal_connect_swapped (self->gesture,
+ "pressed",
+ G_CALLBACK (multipress_pressed_cb),
+ self);
+
+ g_signal_connect_object (self->notification_stack,
+ "changed",
+ G_CALLBACK (ide_omni_bar_notification_stack_changed_cb),
+ self,
+ G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (self->popover,
+ "closed",
+ G_CALLBACK (ide_omni_bar_popover_closed_cb),
+ self,
+ G_CONNECT_SWAPPED);
+
+ gtk_widget_insert_action_group (GTK_WIDGET (self), "omnibar", G_ACTION_GROUP (self));
+
+ ide_widget_set_context_handler (GTK_WIDGET (self), ide_omni_bar_context_set_cb);
+}
+
+GtkWidget *
+ide_omni_bar_new (void)
+{
+ return g_object_new (IDE_TYPE_OMNI_BAR, NULL);
+}
+
+static void
+ide_omni_bar_move_next (IdeOmniBar *self,
+ GVariant *param)
+{
+ g_assert (IDE_IS_OMNI_BAR (self));
+ g_assert (param == NULL);
+
+ ide_notification_stack_move_next (self->notification_stack);
+}
+
+static void
+ide_omni_bar_move_previous (IdeOmniBar *self,
+ GVariant *param)
+{
+ g_assert (IDE_IS_OMNI_BAR (self));
+ g_assert (param == NULL);
+
+ ide_notification_stack_move_previous (self->notification_stack);
+}
+
+/**
+ * ide_omni_bar_add_status_icon:
+ * @self: a #IdeOmniBar
+ * @widget: the #GtkWidget to add
+ * @priority: the sort priority for @widget
+ *
+ * Adds a status-icon style widget to the end of the omnibar. Generally,
+ * you'll want this to be either a GtkButton, GtkLabel, or something simple.
+ *
+ * Since: 3.32
+ */
+void
+ide_omni_bar_add_status_icon (IdeOmniBar *self,
+ GtkWidget *widget,
+ gint priority)
+{
+ g_return_if_fail (IDE_IS_OMNI_BAR (self));
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ gtk_container_add_with_properties (GTK_CONTAINER (self->inner_box), widget,
+ "pack-type", GTK_PACK_END,
+ "priority", priority,
+ NULL);
+}
+
+void
+ide_omni_bar_add_button (IdeOmniBar *self,
+ GtkWidget *widget,
+ GtkPackType pack_type,
+ gint priority)
+{
+ g_return_if_fail (IDE_IS_OMNI_BAR (self));
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+ g_return_if_fail (pack_type == GTK_PACK_START ||
+ pack_type == GTK_PACK_END);
+
+ gtk_container_add_with_properties (GTK_CONTAINER (self->outer_box), widget,
+ "pack-type", pack_type,
+ "priority", priority,
+ NULL);
+}
+
+void
+ide_omni_bar_set_placeholder (IdeOmniBar *self,
+ GtkWidget *widget)
+{
+ g_return_if_fail (IDE_IS_OMNI_BAR (self));
+ g_return_if_fail (!widget || GTK_IS_WIDGET (widget));
+
+ if (self->placeholder == widget)
+ return;
+
+ if (self->placeholder)
+ gtk_widget_destroy (self->placeholder);
+
+ self->placeholder = widget;
+
+ if (self->placeholder)
+ {
+ g_signal_connect (self->placeholder,
+ "destroy",
+ G_CALLBACK (gtk_widget_destroyed),
+ self->placeholder);
+ gtk_container_add_with_properties (GTK_CONTAINER (self->top_stack), self->placeholder,
+ "name", "placeholder",
+ NULL);
+ if (self->notification_stack == NULL ||
+ ide_notification_stack_is_empty (self->notification_stack))
+ gtk_stack_set_visible_child_name (self->top_stack, "placeholder");
+ }
+}
+
+static void
+ide_omni_bar_add_child (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *child,
+ const gchar *type)
+{
+ IdeOmniBar *self = (IdeOmniBar *)buildable;
+
+ g_assert (IDE_IS_OMNI_BAR (self));
+ g_assert (GTK_IS_BUILDER (builder));
+ g_assert (G_IS_OBJECT (child));
+
+ if (ide_str_equal0 (type, "start") && GTK_IS_WIDGET (child))
+ ide_omni_bar_add_button (IDE_OMNI_BAR (self),
+ GTK_WIDGET (child),
+ GTK_PACK_START,
+ 0);
+ else if (ide_str_equal0 (type, "end") && GTK_IS_WIDGET (child))
+ ide_omni_bar_add_button (IDE_OMNI_BAR (self),
+ GTK_WIDGET (child),
+ GTK_PACK_END,
+ 0);
+ else if (ide_str_equal0 (type, "placeholder") && GTK_IS_WIDGET (child))
+ ide_omni_bar_set_placeholder (IDE_OMNI_BAR (self), GTK_WIDGET (child));
+ else
+ parent_buildable_iface->add_child (buildable, builder, child, type);
+}
+
+static void
+buildable_iface_init (GtkBuildableIface *iface)
+{
+ parent_buildable_iface = g_type_interface_peek_parent (iface);
+ iface->add_child = ide_omni_bar_add_child;
+}
+
+/**
+ * ide_omni_bar_add_popover_section:
+ * @self: an #IdeOmniBar
+ * @widget: a #GtkWidget
+ * @priority: sort priority for the section
+ *
+ * Adds @widget to the omnibar popover, sorted by @priority
+ *
+ * Since: 3.32
+ */
+void
+ide_omni_bar_add_popover_section (IdeOmniBar *self,
+ GtkWidget *widget,
+ gint priority)
+{
+ g_return_if_fail (IDE_IS_OMNI_BAR (self));
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ gtk_container_add_with_properties (GTK_CONTAINER (self->sections_box), widget,
+ "priority", priority,
+ NULL);
+}
diff --git a/src/libide/gui/ide-omni-bar.h b/src/libide/gui/ide-omni-bar.h
new file mode 100644
index 000000000..ef4a9e484
--- /dev/null
+++ b/src/libide/gui/ide-omni-bar.h
@@ -0,0 +1,56 @@
+/* ide-omni-bar.h
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#if !defined (IDE_GUI_INSIDE) && !defined (IDE_GUI_COMPILATION)
+# error "Only <libide-gui.h> can be included directly."
+#endif
+
+#include <gtk/gtk.h>
+#include <libide-core.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_OMNI_BAR (ide_omni_bar_get_type())
+
+IDE_AVAILABLE_IN_3_32
+G_DECLARE_FINAL_TYPE (IdeOmniBar, ide_omni_bar, IDE, OMNI_BAR, GtkEventBox)
+
+IDE_AVAILABLE_IN_3_32
+GtkWidget *ide_omni_bar_new (void);
+IDE_AVAILABLE_IN_3_32
+void ide_omni_bar_add_status_icon (IdeOmniBar *self,
+ GtkWidget *widget,
+ gint priority);
+IDE_AVAILABLE_IN_3_32
+void ide_omni_bar_add_button (IdeOmniBar *self,
+ GtkWidget *widget,
+ GtkPackType pack_type,
+ gint priority);
+IDE_AVAILABLE_IN_3_32
+void ide_omni_bar_set_placeholder (IdeOmniBar *self,
+ GtkWidget *placeholder);
+IDE_AVAILABLE_IN_3_32
+void ide_omni_bar_add_popover_section (IdeOmniBar *self,
+ GtkWidget *widget,
+ gint priority);
+
+G_END_DECLS
diff --git a/src/libide/gui/ide-omni-bar.ui b/src/libide/gui/ide-omni-bar.ui
new file mode 100644
index 000000000..53591ff36
--- /dev/null
+++ b/src/libide/gui/ide-omni-bar.ui
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="IdeOmniBar" parent="GtkEventBox">
+ <child>
+ <object class="DzlPriorityBox" id="outer_box">
+ <property name="hexpand">true</property>
+ <property name="visible">true</property>
+ <style>
+ <class name="linked"/>
+ </style>
+ <child type="center">
+ <object class="DzlEntryBox" id="entry_box">
+ <property name="max-width-chars">40</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkOverlay" id="overlay">
+ <property name="visible">true</property>
+ <child type="overlay">
+ <object class="GtkProgressBar" id="progress">
+ <property name="valign">end</property>
+ <property name="hexpand">true</property>
+ <property name="fraction" bind-source="notification_stack" bind-property="progress"/>
+ <property name="visible">true</property>
+ <style>
+ <class name="osd"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="DzlPriorityBox" id="inner_box">
+ <property name="margin-top">1</property>
+ <property name="spacing">3</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkBox">
+ <property name="hexpand">false</property>
+ <property name="vexpand">false</property>
+ <property name="valign">center</property>
+ <property name="orientation">vertical</property>
+ <property name="visible">true</property>
+ <style>
+ <class name="pan"/>>
+ </style>
+ <child>
+ <object class="GtkButton">
+ <property name="action-name">omnibar.move-previous</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkImage">
+ <property name="icon-name">pan-up-symbolic</property>
+ <property name="pixel-size">12</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton">
+ <property name="action-name">omnibar.move-next</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkImage">
+ <property name="icon-name">pan-down-symbolic</property>
+ <property name="pixel-size">12</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkStack" id="top_stack">
+ <property name="margin-start">3</property>
+ <property name="margin-end">3</property>
+ <property name="hexpand">true</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="IdeNotificationStack" id="notification_stack">
+ <property name="visible">true</property>
+ </object>
+ <packing>
+ <property name="name">notifications</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">true</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+ <object class="GtkPopover" id="popover">
+ <property name="width-request">500</property>
+ <property name="relative-to">IdeOmniBar</property>
+ <property name="position">top</property>
+ <style>
+ <class name="omnibar"/>
+ </style>
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <property name="visible">true</property>
+ <child>
+ <object class="DzlPriorityBox" id="sections_box">
+ <property name="orientation">vertical</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBox" id="notifications_list_box">
+ <signal name="row-activated" swapped="true" object="IdeOmniBar"
handler="ide_omni_bar_notification_row_activated"/>
+ <property name="selection-mode">none</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/src/libide/layout/ide-layout-view.c b/src/libide/gui/ide-page.c
similarity index 57%
rename from src/libide/layout/ide-layout-view.c
rename to src/libide/gui/ide-page.c
index d33946fb9..6e6c40925 100644
--- a/src/libide/layout/ide-layout-view.c
+++ b/src/libide/gui/ide-page.c
@@ -1,4 +1,4 @@
-/* ide-layout-view.c
+/* ide-page.c
*
* Copyright 2017-2019 Christian Hergert <chergert redhat com>
*
@@ -18,17 +18,22 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
-#define G_LOG_DOMAIN "ide-layout-view"
+#define G_LOG_DOMAIN "ide-page"
#include "config.h"
+#include <libide-threading.h>
#include <string.h>
-#include "layout/ide-layout-view.h"
-#include "threading/ide-task.h"
+#include "ide-gui-global.h"
+#include "ide-gui-private.h"
+#include "ide-page.h"
+#include "ide-workspace.h"
typedef struct
{
+ GList mru_link;
+
const gchar *menu_id;
const gchar *icon_name;
gchar *title;
@@ -42,7 +47,7 @@ typedef struct
guint can_split : 1;
guint primary_color_bg_set : 1;
guint primary_color_fg_set : 1;
-} IdeLayoutViewPrivate;
+} IdePagePrivate;
enum {
PROP_0,
@@ -59,38 +64,38 @@ enum {
};
enum {
- CREATE_SPLIT_VIEW,
+ CREATE_SPLIT,
N_SIGNALS
};
-G_DEFINE_TYPE_WITH_PRIVATE (IdeLayoutView, ide_layout_view, GTK_TYPE_BOX)
+G_DEFINE_TYPE_WITH_PRIVATE (IdePage, ide_page, GTK_TYPE_BOX)
static GParamSpec *properties [N_PROPS];
static guint signals [N_SIGNALS];
static void
-ide_layout_view_real_agree_to_close_async (IdeLayoutView *self,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+ide_page_real_agree_to_close_async (IdePage *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
g_autoptr(IdeTask) task = NULL;
- g_assert (IDE_IS_LAYOUT_VIEW (self));
+ g_assert (IDE_IS_PAGE (self));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
task = ide_task_new (self, cancellable, callback, user_data);
ide_task_set_priority (task, G_PRIORITY_LOW);
- ide_task_set_source_tag (task, ide_layout_view_agree_to_close_async);
+ ide_task_set_source_tag (task, ide_page_agree_to_close_async);
ide_task_return_boolean (task, TRUE);
}
static gboolean
-ide_layout_view_real_agree_to_close_finish (IdeLayoutView *self,
- GAsyncResult *result,
- GError **error)
+ide_page_real_agree_to_close_finish (IdePage *self,
+ GAsyncResult *result,
+ GError **error)
{
- g_assert (IDE_IS_LAYOUT_VIEW (self));
+ g_assert (IDE_IS_PAGE (self));
g_assert (IDE_IS_TASK (result));
return ide_task_propagate_boolean (IDE_TASK (result), error);
@@ -105,11 +110,11 @@ find_focus_child (GtkWidget *widget,
}
static void
-ide_layout_view_grab_focus (GtkWidget *widget)
+ide_page_grab_focus (GtkWidget *widget)
{
gboolean handled = FALSE;
- g_assert (IDE_IS_LAYOUT_VIEW (widget));
+ g_assert (IDE_IS_PAGE (widget));
/*
* This default grab_focus override just looks for the first child (generally
@@ -122,61 +127,107 @@ ide_layout_view_grab_focus (GtkWidget *widget)
}
static void
-ide_layout_view_finalize (GObject *object)
+ide_page_hierarchy_changed (GtkWidget *widget,
+ GtkWidget *previous_toplevel)
+{
+ IdePage *self = (IdePage *)widget;
+ IdePagePrivate *priv = ide_page_get_instance_private (self);
+ GtkWidget *toplevel;
+
+ g_assert (IDE_IS_PAGE (self));
+ g_assert (!previous_toplevel || GTK_IS_WIDGET (previous_toplevel));
+
+ if (IDE_IS_WORKSPACE (previous_toplevel))
+ _ide_workspace_remove_page_mru (IDE_WORKSPACE (previous_toplevel), &priv->mru_link);
+
+ if (GTK_WIDGET_CLASS (ide_page_parent_class)->hierarchy_changed)
+ GTK_WIDGET_CLASS (ide_page_parent_class)->hierarchy_changed (widget, previous_toplevel);
+
+ toplevel = gtk_widget_get_toplevel (widget);
+
+ if (IDE_IS_WORKSPACE (toplevel))
+ _ide_workspace_add_page_mru (IDE_WORKSPACE (toplevel), &priv->mru_link);
+}
+
+/**
+ * ide_page_mark_used:
+ * @self: a #IdePage
+ *
+ * This function marks the page as used by updating it's position in the
+ * workspaces MRU (most-recently-used) queue.
+ *
+ * Pages should call this when their contents have been focused.
+ *
+ * Since: 3.32
+ */
+void
+ide_page_mark_used (IdePage *self)
+{
+ IdePagePrivate *priv = ide_page_get_instance_private (self);
+ IdeWorkspace *workspace;
+
+ g_return_if_fail (IDE_IS_PAGE (self));
+
+ if ((workspace = ide_widget_get_workspace (GTK_WIDGET (self))))
+ _ide_workspace_move_front_page_mru (workspace, &priv->mru_link);
+}
+
+static void
+ide_page_finalize (GObject *object)
{
- IdeLayoutView *self = (IdeLayoutView *)object;
- IdeLayoutViewPrivate *priv = ide_layout_view_get_instance_private (self);
+ IdePage *self = (IdePage *)object;
+ IdePagePrivate *priv = ide_page_get_instance_private (self);
g_clear_pointer (&priv->title, g_free);
g_clear_object (&priv->icon);
- G_OBJECT_CLASS (ide_layout_view_parent_class)->finalize (object);
+ G_OBJECT_CLASS (ide_page_parent_class)->finalize (object);
}
static void
-ide_layout_view_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
+ide_page_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
{
- IdeLayoutView *self = IDE_LAYOUT_VIEW (object);
+ IdePage *self = IDE_PAGE (object);
switch (prop_id)
{
case PROP_CAN_SPLIT:
- g_value_set_boolean (value, ide_layout_view_get_can_split (self));
+ g_value_set_boolean (value, ide_page_get_can_split (self));
break;
case PROP_FAILED:
- g_value_set_boolean (value, ide_layout_view_get_failed (self));
+ g_value_set_boolean (value, ide_page_get_failed (self));
break;
case PROP_ICON_NAME:
- g_value_set_static_string (value, ide_layout_view_get_icon_name (self));
+ g_value_set_static_string (value, ide_page_get_icon_name (self));
break;
case PROP_ICON:
- g_value_set_object (value, ide_layout_view_get_icon (self));
+ g_value_set_object (value, ide_page_get_icon (self));
break;
case PROP_MENU_ID:
- g_value_set_static_string (value, ide_layout_view_get_menu_id (self));
+ g_value_set_static_string (value, ide_page_get_menu_id (self));
break;
case PROP_MODIFIED:
- g_value_set_boolean (value, ide_layout_view_get_modified (self));
+ g_value_set_boolean (value, ide_page_get_modified (self));
break;
case PROP_PRIMARY_COLOR_BG:
- g_value_set_boxed (value, ide_layout_view_get_primary_color_bg (self));
+ g_value_set_boxed (value, ide_page_get_primary_color_bg (self));
break;
case PROP_PRIMARY_COLOR_FG:
- g_value_set_boxed (value, ide_layout_view_get_primary_color_fg (self));
+ g_value_set_boxed (value, ide_page_get_primary_color_fg (self));
break;
case PROP_TITLE:
- g_value_set_string (value, ide_layout_view_get_title (self));
+ g_value_set_string (value, ide_page_get_title (self));
break;
default:
@@ -185,49 +236,49 @@ ide_layout_view_get_property (GObject *object,
}
static void
-ide_layout_view_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
+ide_page_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
{
- IdeLayoutView *self = IDE_LAYOUT_VIEW (object);
+ IdePage *self = IDE_PAGE (object);
switch (prop_id)
{
case PROP_CAN_SPLIT:
- ide_layout_view_set_can_split (self, g_value_get_boolean (value));
+ ide_page_set_can_split (self, g_value_get_boolean (value));
break;
case PROP_FAILED:
- ide_layout_view_set_failed (self, g_value_get_boolean (value));
+ ide_page_set_failed (self, g_value_get_boolean (value));
break;
case PROP_ICON_NAME:
- ide_layout_view_set_icon_name (self, g_value_get_string (value));
+ ide_page_set_icon_name (self, g_value_get_string (value));
break;
case PROP_ICON:
- ide_layout_view_set_icon (self, g_value_get_object (value));
+ ide_page_set_icon (self, g_value_get_object (value));
break;
case PROP_MENU_ID:
- ide_layout_view_set_menu_id (self, g_value_get_string (value));
+ ide_page_set_menu_id (self, g_value_get_string (value));
break;
case PROP_MODIFIED:
- ide_layout_view_set_modified (self, g_value_get_boolean (value));
+ ide_page_set_modified (self, g_value_get_boolean (value));
break;
case PROP_PRIMARY_COLOR_BG:
- ide_layout_view_set_primary_color_bg (self, g_value_get_boxed (value));
+ ide_page_set_primary_color_bg (self, g_value_get_boxed (value));
break;
case PROP_PRIMARY_COLOR_FG:
- ide_layout_view_set_primary_color_fg (self, g_value_get_boxed (value));
+ ide_page_set_primary_color_fg (self, g_value_get_boxed (value));
break;
case PROP_TITLE:
- ide_layout_view_set_title (self, g_value_get_string (value));
+ ide_page_set_title (self, g_value_get_string (value));
break;
default:
@@ -236,19 +287,20 @@ ide_layout_view_set_property (GObject *object,
}
static void
-ide_layout_view_class_init (IdeLayoutViewClass *klass)
+ide_page_class_init (IdePageClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
- object_class->finalize = ide_layout_view_finalize;
- object_class->get_property = ide_layout_view_get_property;
- object_class->set_property = ide_layout_view_set_property;
+ object_class->finalize = ide_page_finalize;
+ object_class->get_property = ide_page_get_property;
+ object_class->set_property = ide_page_set_property;
- widget_class->grab_focus = ide_layout_view_grab_focus;
+ widget_class->grab_focus = ide_page_grab_focus;
+ widget_class->hierarchy_changed = ide_page_hierarchy_changed;
- klass->agree_to_close_async = ide_layout_view_real_agree_to_close_async;
- klass->agree_to_close_finish = ide_layout_view_real_agree_to_close_finish;
+ klass->agree_to_close_async = ide_page_real_agree_to_close_async;
+ klass->agree_to_close_finish = ide_page_real_agree_to_close_finish;
properties [PROP_CAN_SPLIT] =
g_param_spec_boolean ("can-split",
@@ -293,7 +345,7 @@ ide_layout_view_class_init (IdeLayoutViewClass *klass)
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
/**
- * IdeLayoutView:primary-color-bg:
+ * IdePage:primary-color-bg:
*
* The "primary-color-bg" property should describe the primary color
* of the content of the view (if any).
@@ -311,7 +363,7 @@ ide_layout_view_class_init (IdeLayoutViewClass *klass)
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
/**
- * IdeLayoutView:primary-color-fg:
+ * IdePage:primary-color-fg:
*
* The "primary-color-fg" property should describe the foreground
* to use for content above primary-color-bg.
@@ -338,36 +390,38 @@ ide_layout_view_class_init (IdeLayoutViewClass *klass)
g_object_class_install_properties (object_class, N_PROPS, properties);
/**
- * IdeLayoutView::create-split-view:
+ * IdePage::create-split:
+ * @self: an #IdePage
*
* This signal is emitted when the view is requested to make a split
* version of itself. This happens when the user requests that a second
* version of the file to be displayed, often side-by-side.
*
- * This signal will only be emitted when #IdeLayoutView:can-split is
+ * This signal will only be emitted when #IdePage:can-split is
* set to %TRUE. The default is %FALSE.
*
- * Returns: (transfer full): A newly created #IdeLayoutView
+ * Returns: (transfer full): A newly created #IdePage
*
* Since: 3.32
*/
- signals [CREATE_SPLIT_VIEW] =
- g_signal_new (g_intern_static_string ("create-split-view"),
+ signals [CREATE_SPLIT] =
+ g_signal_new (g_intern_static_string ("create-split"),
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (IdeLayoutViewClass, create_split_view),
+ G_STRUCT_OFFSET (IdePageClass, create_split),
g_signal_accumulator_first_wins, NULL,
- NULL, IDE_TYPE_LAYOUT_VIEW, 0);
+ NULL, IDE_TYPE_PAGE, 0);
}
static void
-ide_layout_view_init (IdeLayoutView *self)
+ide_page_init (IdePage *self)
{
- IdeLayoutViewPrivate *priv = ide_layout_view_get_instance_private (self);
+ IdePagePrivate *priv = ide_page_get_instance_private (self);
g_autoptr(GSimpleActionGroup) group = g_simple_action_group_new ();
gtk_orientable_set_orientation (GTK_ORIENTABLE (self), GTK_ORIENTATION_VERTICAL);
+ priv->mru_link.data = self;
priv->icon_name = g_intern_string ("text-x-generic-symbolic");
/* Add an action group out of convenience to plugins that want to
@@ -377,28 +431,28 @@ ide_layout_view_init (IdeLayoutView *self)
}
GtkWidget *
-ide_layout_view_new (void)
+ide_page_new (void)
{
- return g_object_new (IDE_TYPE_LAYOUT_VIEW, NULL);
+ return g_object_new (IDE_TYPE_PAGE, NULL);
}
const gchar *
-ide_layout_view_get_title (IdeLayoutView *self)
+ide_page_get_title (IdePage *self)
{
- IdeLayoutViewPrivate *priv = ide_layout_view_get_instance_private (self);
+ IdePagePrivate *priv = ide_page_get_instance_private (self);
- g_return_val_if_fail (IDE_IS_LAYOUT_VIEW (self), NULL);
+ g_return_val_if_fail (IDE_IS_PAGE (self), NULL);
return priv->title;
}
void
-ide_layout_view_set_title (IdeLayoutView *self,
- const gchar *title)
+ide_page_set_title (IdePage *self,
+ const gchar *title)
{
- IdeLayoutViewPrivate *priv = ide_layout_view_get_instance_private (self);
+ IdePagePrivate *priv = ide_page_get_instance_private (self);
- g_return_if_fail (IDE_IS_LAYOUT_VIEW (self));
+ g_return_if_fail (IDE_IS_PAGE (self));
if (g_strcmp0 (title, priv->title) != 0)
{
@@ -409,22 +463,22 @@ ide_layout_view_set_title (IdeLayoutView *self,
}
const gchar *
-ide_layout_view_get_menu_id (IdeLayoutView *self)
+ide_page_get_menu_id (IdePage *self)
{
- IdeLayoutViewPrivate *priv = ide_layout_view_get_instance_private (self);
+ IdePagePrivate *priv = ide_page_get_instance_private (self);
- g_return_val_if_fail (IDE_IS_LAYOUT_VIEW (self), NULL);
+ g_return_val_if_fail (IDE_IS_PAGE (self), NULL);
return priv->menu_id;
}
void
-ide_layout_view_set_menu_id (IdeLayoutView *self,
- const gchar *menu_id)
+ide_page_set_menu_id (IdePage *self,
+ const gchar *menu_id)
{
- IdeLayoutViewPrivate *priv = ide_layout_view_get_instance_private (self);
+ IdePagePrivate *priv = ide_page_get_instance_private (self);
- g_return_if_fail (IDE_IS_LAYOUT_VIEW (self));
+ g_return_if_fail (IDE_IS_PAGE (self));
menu_id = g_intern_string (menu_id);
@@ -436,45 +490,45 @@ ide_layout_view_set_menu_id (IdeLayoutView *self,
}
void
-ide_layout_view_agree_to_close_async (IdeLayoutView *self,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+ide_page_agree_to_close_async (IdePage *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- g_return_if_fail (IDE_IS_LAYOUT_VIEW (self));
+ g_return_if_fail (IDE_IS_PAGE (self));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
- IDE_LAYOUT_VIEW_GET_CLASS (self)->agree_to_close_async (self, cancellable, callback, user_data);
+ IDE_PAGE_GET_CLASS (self)->agree_to_close_async (self, cancellable, callback, user_data);
}
gboolean
-ide_layout_view_agree_to_close_finish (IdeLayoutView *self,
- GAsyncResult *result,
- GError **error)
+ide_page_agree_to_close_finish (IdePage *self,
+ GAsyncResult *result,
+ GError **error)
{
- g_return_val_if_fail (IDE_IS_LAYOUT_VIEW (self), FALSE);
+ g_return_val_if_fail (IDE_IS_PAGE (self), FALSE);
g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
- return IDE_LAYOUT_VIEW_GET_CLASS (self)->agree_to_close_finish (self, result, error);
+ return IDE_PAGE_GET_CLASS (self)->agree_to_close_finish (self, result, error);
}
gboolean
-ide_layout_view_get_failed (IdeLayoutView *self)
+ide_page_get_failed (IdePage *self)
{
- IdeLayoutViewPrivate *priv = ide_layout_view_get_instance_private (self);
+ IdePagePrivate *priv = ide_page_get_instance_private (self);
- g_return_val_if_fail (IDE_IS_LAYOUT_VIEW (self), FALSE);
+ g_return_val_if_fail (IDE_IS_PAGE (self), FALSE);
return priv->failed;
}
void
-ide_layout_view_set_failed (IdeLayoutView *self,
- gboolean failed)
+ide_page_set_failed (IdePage *self,
+ gboolean failed)
{
- IdeLayoutViewPrivate *priv = ide_layout_view_get_instance_private (self);
+ IdePagePrivate *priv = ide_page_get_instance_private (self);
- g_return_if_fail (IDE_IS_LAYOUT_VIEW (self));
+ g_return_if_fail (IDE_IS_PAGE (self));
failed = !!failed;
@@ -486,22 +540,22 @@ ide_layout_view_set_failed (IdeLayoutView *self,
}
gboolean
-ide_layout_view_get_modified (IdeLayoutView *self)
+ide_page_get_modified (IdePage *self)
{
- IdeLayoutViewPrivate *priv = ide_layout_view_get_instance_private (self);
+ IdePagePrivate *priv = ide_page_get_instance_private (self);
- g_return_val_if_fail (IDE_IS_LAYOUT_VIEW (self), FALSE);
+ g_return_val_if_fail (IDE_IS_PAGE (self), FALSE);
return priv->modified;
}
void
-ide_layout_view_set_modified (IdeLayoutView *self,
- gboolean modified)
+ide_page_set_modified (IdePage *self,
+ gboolean modified)
{
- IdeLayoutViewPrivate *priv = ide_layout_view_get_instance_private (self);
+ IdePagePrivate *priv = ide_page_get_instance_private (self);
- g_return_if_fail (IDE_IS_LAYOUT_VIEW (self));
+ g_return_if_fail (IDE_IS_PAGE (self));
modified = !!modified;
@@ -513,8 +567,8 @@ ide_layout_view_set_modified (IdeLayoutView *self,
}
/**
- * ide_layout_view_get_icon:
- * @self: a #IdeLayoutView
+ * ide_page_get_icon:
+ * @self: a #IdePage
*
* Gets the #GIcon to represent the view.
*
@@ -523,11 +577,11 @@ ide_layout_view_set_modified (IdeLayoutView *self,
* Since: 3.32
*/
GIcon *
-ide_layout_view_get_icon (IdeLayoutView *self)
+ide_page_get_icon (IdePage *self)
{
- IdeLayoutViewPrivate *priv = ide_layout_view_get_instance_private (self);
+ IdePagePrivate *priv = ide_page_get_instance_private (self);
- g_return_val_if_fail (IDE_IS_LAYOUT_VIEW (self), NULL);
+ g_return_val_if_fail (IDE_IS_PAGE (self), NULL);
if (priv->icon == NULL)
{
@@ -539,34 +593,34 @@ ide_layout_view_get_icon (IdeLayoutView *self)
}
void
-ide_layout_view_set_icon (IdeLayoutView *self,
- GIcon *icon)
+ide_page_set_icon (IdePage *self,
+ GIcon *icon)
{
- IdeLayoutViewPrivate *priv = ide_layout_view_get_instance_private (self);
+ IdePagePrivate *priv = ide_page_get_instance_private (self);
- g_return_if_fail (IDE_IS_LAYOUT_VIEW (self));
+ g_return_if_fail (IDE_IS_PAGE (self));
if (g_set_object (&priv->icon, icon))
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ICON]);
}
const gchar *
-ide_layout_view_get_icon_name (IdeLayoutView *self)
+ide_page_get_icon_name (IdePage *self)
{
- IdeLayoutViewPrivate *priv = ide_layout_view_get_instance_private (self);
+ IdePagePrivate *priv = ide_page_get_instance_private (self);
- g_return_val_if_fail (IDE_IS_LAYOUT_VIEW (self), NULL);
+ g_return_val_if_fail (IDE_IS_PAGE (self), NULL);
return priv->icon_name;
}
void
-ide_layout_view_set_icon_name (IdeLayoutView *self,
- const gchar *icon_name)
+ide_page_set_icon_name (IdePage *self,
+ const gchar *icon_name)
{
- IdeLayoutViewPrivate *priv = ide_layout_view_get_instance_private (self);
+ IdePagePrivate *priv = ide_page_get_instance_private (self);
- g_return_if_fail (IDE_IS_LAYOUT_VIEW (self));
+ g_return_if_fail (IDE_IS_PAGE (self));
icon_name = g_intern_string (icon_name);
@@ -579,22 +633,22 @@ ide_layout_view_set_icon_name (IdeLayoutView *self,
}
gboolean
-ide_layout_view_get_can_split (IdeLayoutView *self)
+ide_page_get_can_split (IdePage *self)
{
- IdeLayoutViewPrivate *priv = ide_layout_view_get_instance_private (self);
+ IdePagePrivate *priv = ide_page_get_instance_private (self);
- g_return_val_if_fail (IDE_IS_LAYOUT_VIEW (self), FALSE);
+ g_return_val_if_fail (IDE_IS_PAGE (self), FALSE);
return priv->can_split;
}
void
-ide_layout_view_set_can_split (IdeLayoutView *self,
- gboolean can_split)
+ide_page_set_can_split (IdePage *self,
+ gboolean can_split)
{
- IdeLayoutViewPrivate *priv = ide_layout_view_get_instance_private (self);
+ IdePagePrivate *priv = ide_page_get_instance_private (self);
- g_return_if_fail (IDE_IS_LAYOUT_VIEW (self));
+ g_return_if_fail (IDE_IS_PAGE (self));
can_split = !!can_split;
@@ -606,40 +660,40 @@ ide_layout_view_set_can_split (IdeLayoutView *self,
}
/**
- * ide_layout_view_create_split_view:
- * @self: an #IdeLayoutView
+ * ide_page_create_split:
+ * @self: an #IdePage
*
- * This function requests that the #IdeLayoutView create a split version
+ * This function requests that the #IdePage create a split version
* of itself so that the user may view the document in multiple views.
*
* The view should be added to an #IdeLayoutStack where appropriate.
*
- * Returns: (nullable) (transfer full): A newly created #IdeLayoutView or %NULL.
+ * Returns: (nullable) (transfer full): A newly created #IdePage or %NULL.
*
* Since: 3.32
*/
-IdeLayoutView *
-ide_layout_view_create_split_view (IdeLayoutView *self)
+IdePage *
+ide_page_create_split (IdePage *self)
{
- IdeLayoutViewPrivate *priv = ide_layout_view_get_instance_private (self);
- IdeLayoutView *ret = NULL;
+ IdePagePrivate *priv = ide_page_get_instance_private (self);
+ IdePage *ret = NULL;
- g_return_val_if_fail (IDE_IS_LAYOUT_VIEW (self), NULL);
+ g_return_val_if_fail (IDE_IS_PAGE (self), NULL);
if (priv->can_split)
{
- g_signal_emit (self, signals [CREATE_SPLIT_VIEW], 0, &ret);
- g_return_val_if_fail (!ret || IDE_IS_LAYOUT_VIEW (ret), NULL);
+ g_signal_emit (self, signals [CREATE_SPLIT], 0, &ret);
+ g_return_val_if_fail (!ret || IDE_IS_PAGE (ret), NULL);
}
return ret;
}
/**
- * ide_layout_view_get_primary_color_bg:
- * @self: a #IdeLayoutView
+ * ide_page_get_primary_color_bg:
+ * @self: a #IdePage
*
- * Gets the #IdeLayoutView:primary-color-bg property if it has been set.
+ * Gets the #IdePage:primary-color-bg property if it has been set.
*
* The primary-color-bg can be used to alter the color of the layout
* stack header to match the document contents.
@@ -649,34 +703,34 @@ ide_layout_view_create_split_view (IdeLayoutView *self)
* Since: 3.32
*/
const GdkRGBA *
-ide_layout_view_get_primary_color_bg (IdeLayoutView *self)
+ide_page_get_primary_color_bg (IdePage *self)
{
- IdeLayoutViewPrivate *priv = ide_layout_view_get_instance_private (self);
+ IdePagePrivate *priv = ide_page_get_instance_private (self);
- g_return_val_if_fail (IDE_IS_LAYOUT_VIEW (self), NULL);
+ g_return_val_if_fail (IDE_IS_PAGE (self), NULL);
return priv->primary_color_bg_set ? &priv->primary_color_bg : NULL;
}
/**
- * ide_layout_view_set_primary_color_bg:
- * @self: a #IdeLayoutView
+ * ide_page_set_primary_color_bg:
+ * @self: a #IdePage
* @primary_color_bg: (nullable): a #GdkRGBA or %NULL
*
- * Sets the #IdeLayoutView:primary-color-bg property.
+ * Sets the #IdePage:primary-color-bg property.
* If @primary_color_bg is %NULL, the property is unset.
*
* Since: 3.32
*/
void
-ide_layout_view_set_primary_color_bg (IdeLayoutView *self,
- const GdkRGBA *primary_color_bg)
+ide_page_set_primary_color_bg (IdePage *self,
+ const GdkRGBA *primary_color_bg)
{
- IdeLayoutViewPrivate *priv = ide_layout_view_get_instance_private (self);
+ IdePagePrivate *priv = ide_page_get_instance_private (self);
gboolean old_set;
GdkRGBA old;
- g_return_if_fail (IDE_IS_LAYOUT_VIEW (self));
+ g_return_if_fail (IDE_IS_PAGE (self));
old_set = priv->primary_color_bg_set;
old = priv->primary_color_bg;
@@ -698,10 +752,10 @@ ide_layout_view_set_primary_color_bg (IdeLayoutView *self,
}
/**
- * ide_layout_view_get_primary_color_fg:
- * @self: a #IdeLayoutView
+ * ide_page_get_primary_color_fg:
+ * @self: a #IdePage
*
- * Gets the #IdeLayoutView:primary-color-fg property if it has been set.
+ * Gets the #IdePage:primary-color-fg property if it has been set.
*
* The primary-color-fg can be used to alter the foreground color of the layout
* stack header to match the document contents.
@@ -711,34 +765,34 @@ ide_layout_view_set_primary_color_bg (IdeLayoutView *self,
* Since: 3.32
*/
const GdkRGBA *
-ide_layout_view_get_primary_color_fg (IdeLayoutView *self)
+ide_page_get_primary_color_fg (IdePage *self)
{
- IdeLayoutViewPrivate *priv = ide_layout_view_get_instance_private (self);
+ IdePagePrivate *priv = ide_page_get_instance_private (self);
- g_return_val_if_fail (IDE_IS_LAYOUT_VIEW (self), NULL);
+ g_return_val_if_fail (IDE_IS_PAGE (self), NULL);
return priv->primary_color_fg_set ? &priv->primary_color_fg : NULL;
}
/**
- * ide_layout_view_set_primary_color_fg:
- * @self: a #IdeLayoutView
+ * ide_page_set_primary_color_fg:
+ * @self: a #IdePage
* @primary_color_fg: (nullable): a #GdkRGBA or %NULL
*
- * Sets the #IdeLayoutView:primary-color-fg property.
+ * Sets the #IdePage:primary-color-fg property.
* If @primary_color_fg is %NULL, the property is unset.
*
* Since: 3.32
*/
void
-ide_layout_view_set_primary_color_fg (IdeLayoutView *self,
- const GdkRGBA *primary_color_fg)
+ide_page_set_primary_color_fg (IdePage *self,
+ const GdkRGBA *primary_color_fg)
{
- IdeLayoutViewPrivate *priv = ide_layout_view_get_instance_private (self);
+ IdePagePrivate *priv = ide_page_get_instance_private (self);
gboolean old_set;
GdkRGBA old;
- g_return_if_fail (IDE_IS_LAYOUT_VIEW (self));
+ g_return_if_fail (IDE_IS_PAGE (self));
old_set = priv->primary_color_fg_set;
old = priv->primary_color_fg;
@@ -760,8 +814,8 @@ ide_layout_view_set_primary_color_fg (IdeLayoutView *self,
}
/**
- * ide_layout_view_report_error:
- * @self: a #IdeLayoutView
+ * ide_page_report_error:
+ * @self: a #IdePage
* @format: a printf-style format string
*
* This function reports an error to the user in the layout view.
@@ -772,9 +826,9 @@ ide_layout_view_set_primary_color_fg (IdeLayoutView *self,
* Since: 3.32
*/
void
-ide_layout_view_report_error (IdeLayoutView *self,
- const gchar *format,
- ...)
+ide_page_report_error (IdePage *self,
+ const gchar *format,
+ ...)
{
g_autofree gchar *message = NULL;
GtkInfoBar *infobar;
@@ -782,7 +836,7 @@ ide_layout_view_report_error (IdeLayoutView *self,
GtkLabel *label;
va_list args;
- g_return_if_fail (IDE_IS_LAYOUT_VIEW (self));
+ g_return_if_fail (IDE_IS_PAGE (self));
va_start (args, format);
message = g_strdup_vprintf (format, args);
diff --git a/src/libide/gui/ide-page.h b/src/libide/gui/ide-page.h
new file mode 100644
index 000000000..3a5c34631
--- /dev/null
+++ b/src/libide/gui/ide-page.h
@@ -0,0 +1,119 @@
+/* ide-page.h
+ *
+ * Copyright 2017-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#if !defined (IDE_GUI_INSIDE) && !defined (IDE_GUI_COMPILATION)
+# error "Only <libide-gui.h> can be included directly."
+#endif
+
+#include <gtk/gtk.h>
+#include <libide-core.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_PAGE (ide_page_get_type())
+
+IDE_AVAILABLE_IN_3_32
+G_DECLARE_DERIVABLE_TYPE (IdePage, ide_page, IDE, PAGE, GtkBox)
+
+struct _IdePageClass
+{
+ GtkBoxClass parent_class;
+
+ void (*agree_to_close_async) (IdePage *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (*agree_to_close_finish) (IdePage *self,
+ GAsyncResult *result,
+ GError **error);
+ IdePage *(*create_split) (IdePage *self);
+
+ /*< private >*/
+ gpointer _reserved[16];
+};
+
+IDE_AVAILABLE_IN_3_32
+GtkWidget *ide_page_new (void);
+IDE_AVAILABLE_IN_3_32
+gboolean ide_page_get_can_split (IdePage *self);
+IDE_AVAILABLE_IN_3_32
+void ide_page_set_can_split (IdePage *self,
+ gboolean can_split);
+IDE_AVAILABLE_IN_3_32
+IdePage *ide_page_create_split (IdePage *self);
+IDE_AVAILABLE_IN_3_32
+const gchar *ide_page_get_icon_name (IdePage *self);
+IDE_AVAILABLE_IN_3_32
+void ide_page_set_icon_name (IdePage *self,
+ const gchar *icon_name);
+IDE_AVAILABLE_IN_3_32
+GIcon *ide_page_get_icon (IdePage *self);
+IDE_AVAILABLE_IN_3_32
+void ide_page_set_icon (IdePage *self,
+ GIcon *icon);
+IDE_AVAILABLE_IN_3_32
+gboolean ide_page_get_failed (IdePage *self);
+IDE_AVAILABLE_IN_3_32
+void ide_page_set_failed (IdePage *self,
+ gboolean failed);
+IDE_AVAILABLE_IN_3_32
+const gchar *ide_page_get_menu_id (IdePage *self);
+IDE_AVAILABLE_IN_3_32
+void ide_page_set_menu_id (IdePage *self,
+ const gchar *menu_id);
+IDE_AVAILABLE_IN_3_32
+gboolean ide_page_get_modified (IdePage *self);
+IDE_AVAILABLE_IN_3_32
+void ide_page_set_modified (IdePage *self,
+ gboolean modified);
+IDE_AVAILABLE_IN_3_32
+const gchar *ide_page_get_title (IdePage *self);
+IDE_AVAILABLE_IN_3_32
+void ide_page_set_title (IdePage *self,
+ const gchar *title);
+IDE_AVAILABLE_IN_3_32
+const GdkRGBA *ide_page_get_primary_color_bg (IdePage *self);
+IDE_AVAILABLE_IN_3_32
+void ide_page_set_primary_color_bg (IdePage *self,
+ const GdkRGBA *primary_color_bg);
+IDE_AVAILABLE_IN_3_32
+const GdkRGBA *ide_page_get_primary_color_fg (IdePage *self);
+IDE_AVAILABLE_IN_3_32
+void ide_page_set_primary_color_fg (IdePage *self,
+ const GdkRGBA *primary_color_fg);
+IDE_AVAILABLE_IN_3_32
+void ide_page_agree_to_close_async (IdePage *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+IDE_AVAILABLE_IN_3_32
+gboolean ide_page_agree_to_close_finish (IdePage *self,
+ GAsyncResult *result,
+ GError **error);
+IDE_AVAILABLE_IN_3_32
+void ide_page_mark_used (IdePage *self);
+IDE_AVAILABLE_IN_3_32
+void ide_page_report_error (IdePage *self,
+ const gchar *format,
+ ...) G_GNUC_PRINTF (2, 3);
+
+G_END_DECLS
diff --git a/src/libide/gui/ide-pane.c b/src/libide/gui/ide-pane.c
new file mode 100644
index 000000000..bb1c7ec0a
--- /dev/null
+++ b/src/libide/gui/ide-pane.c
@@ -0,0 +1,54 @@
+/* ide-pane.c
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-pane"
+
+#include "config.h"
+
+#include "ide-pane.h"
+
+G_DEFINE_TYPE (IdePane, ide_pane, DZL_TYPE_DOCK_WIDGET)
+
+static void
+ide_pane_class_init (IdePaneClass *klass)
+{
+}
+
+static void
+ide_pane_init (IdePane *self)
+{
+}
+
+/**
+ * ide_pane_new:
+ *
+ * Creates a new #IdePane widget.
+ *
+ * These widgets are meant to be added to #IdePanel widgets.
+ *
+ * Returns: (transfer full): a new #IdePane
+ *
+ * Since: 3.32
+ */
+GtkWidget *
+ide_pane_new (void)
+{
+ return g_object_new (IDE_TYPE_PANE, NULL);
+}
diff --git a/src/libide/gui/ide-pane.h b/src/libide/gui/ide-pane.h
new file mode 100644
index 000000000..0eed763d5
--- /dev/null
+++ b/src/libide/gui/ide-pane.h
@@ -0,0 +1,48 @@
+/* ide-pane.h
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#if !defined (IDE_GUI_INSIDE) && !defined (IDE_GUI_COMPILATION)
+# error "Only <libide-gui.h> can be included directly."
+#endif
+
+#include <dazzle.h>
+#include <libide-core.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_PANE (ide_pane_get_type())
+
+IDE_AVAILABLE_IN_3_32
+G_DECLARE_DERIVABLE_TYPE (IdePane, ide_pane, IDE, PANE, DzlDockWidget)
+
+struct _IdePaneClass
+{
+ DzlDockWidgetClass parent_class;
+
+ /*< private >*/
+ gpointer _reserved[16];
+};
+
+IDE_AVAILABLE_IN_3_32
+GtkWidget *ide_pane_new (void);
+
+G_END_DECLS
diff --git a/src/libide/layout/ide-layout-pane.c b/src/libide/gui/ide-panel.c
similarity index 59%
rename from src/libide/layout/ide-layout-pane.c
rename to src/libide/gui/ide-panel.c
index 59d848e04..e3091fd85 100644
--- a/src/libide/layout/ide-layout-pane.c
+++ b/src/libide/gui/ide-panel.c
@@ -1,4 +1,4 @@
-/* ide-layout-pane.c
+/* ide-panel.c
*
* Copyright 2015-2019 Christian Hergert <christian hergert me>
*
@@ -18,51 +18,68 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
-#define G_LOG_DOMAIN "ide-layout-pane"
+#define G_LOG_DOMAIN "ide-panel"
#include "config.h"
#include <dazzle.h>
#include <glib/gi18n.h>
-#include "layout/ide-layout-pane.h"
+#include "ide-panel.h"
typedef struct
{
DzlDockStack *dock_stack;
-} IdeLayoutPanePrivate;
+} IdePanelPrivate;
-G_DEFINE_TYPE_WITH_PRIVATE (IdeLayoutPane, ide_layout_pane, DZL_TYPE_DOCK_BIN_EDGE)
+G_DEFINE_TYPE_WITH_PRIVATE (IdePanel, ide_panel, DZL_TYPE_DOCK_BIN_EDGE)
static void
-ide_layout_pane_add (GtkContainer *container,
- GtkWidget *widget)
+ide_panel_add (GtkContainer *container,
+ GtkWidget *widget)
{
- IdeLayoutPane *self = (IdeLayoutPane *)container;
- IdeLayoutPanePrivate *priv = ide_layout_pane_get_instance_private (self);
+ IdePanel *self = (IdePanel *)container;
+ IdePanelPrivate *priv = ide_panel_get_instance_private (self);
- g_assert (IDE_IS_LAYOUT_PANE (self));
+ g_assert (IDE_IS_PANEL (self));
if (DZL_IS_DOCK_WIDGET (widget))
gtk_container_add (GTK_CONTAINER (priv->dock_stack), widget);
else
- GTK_CONTAINER_CLASS (ide_layout_pane_parent_class)->add (container, widget);
+ GTK_CONTAINER_CLASS (ide_panel_parent_class)->add (container, widget);
}
static void
-ide_layout_pane_class_init (IdeLayoutPaneClass *klass)
+ide_panel_class_init (IdePanelClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
- container_class->add = ide_layout_pane_add;
+ container_class->add = ide_panel_add;
- gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/builder/ui/ide-layout-pane.ui");
- gtk_widget_class_bind_template_child_private (widget_class, IdeLayoutPane, dock_stack);
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/libide-gui/ui/ide-panel.ui");
+ gtk_widget_class_bind_template_child_private (widget_class, IdePanel, dock_stack);
}
static void
-ide_layout_pane_init (IdeLayoutPane *self)
+ide_panel_init (IdePanel *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
}
+
+/**
+ * ide_panel_new:
+ *
+ * Creates a new #IdePanel widget.
+ *
+ * These are meant to be added to #IdeSurface widgets within a workspace.
+ *
+ * Returns: an #IdePanel
+ *
+ * Since: 3.32
+ */
+GtkWidget *
+ide_panel_new (void)
+{
+ return g_object_new (IDE_TYPE_PANEL, NULL);
+}
diff --git a/src/libide/layout/ide-layout-pane.h b/src/libide/gui/ide-panel.h
similarity index 65%
rename from src/libide/layout/ide-layout-pane.h
rename to src/libide/gui/ide-panel.h
index 82c19c057..50f99acc3 100644
--- a/src/libide/layout/ide-layout-pane.h
+++ b/src/libide/gui/ide-panel.h
@@ -1,6 +1,6 @@
-/* ide-layout-pane.h
+/* ide-panel.h
*
- * Copyright 2015-2019 Christian Hergert <christian hergert me>
+ * Copyright 2017-2019 Christian Hergert <chergert redhat com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,23 +20,29 @@
#pragma once
-#include <dazzle.h>
+#if !defined (IDE_GUI_INSIDE) && !defined (IDE_GUI_COMPILATION)
+# error "Only <libide-gui.h> can be included directly."
+#endif
-#include "ide-version-macros.h"
+#include <dazzle.h>
+#include <libide-core.h>
G_BEGIN_DECLS
-#define IDE_TYPE_LAYOUT_PANE (ide_layout_pane_get_type())
+#define IDE_TYPE_PANEL (ide_panel_get_type())
IDE_AVAILABLE_IN_3_32
-G_DECLARE_DERIVABLE_TYPE (IdeLayoutPane, ide_layout_pane, IDE, LAYOUT_PANE, DzlDockBinEdge)
+G_DECLARE_DERIVABLE_TYPE (IdePanel, ide_panel, IDE, PANEL, DzlDockBinEdge)
-struct _IdeLayoutPaneClass
+struct _IdePanelClass
{
DzlDockBinEdgeClass parent_class;
/*< private >*/
- gpointer _reserved[8];
+ gpointer _reserved[16];
};
+IDE_AVAILABLE_IN_3_32
+GtkWidget *ide_panel_new (void);
+
G_END_DECLS
diff --git a/src/libide/layout/ide-layout-pane.ui b/src/libide/gui/ide-panel.ui
similarity index 73%
rename from src/libide/layout/ide-layout-pane.ui
rename to src/libide/gui/ide-panel.ui
index 83856bff3..4fd94fc62 100644
--- a/src/libide/layout/ide-layout-pane.ui
+++ b/src/libide/gui/ide-panel.ui
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
- <!-- interface-requires gtk+ 3.16 -->
- <template class="IdeLayoutPane" parent="DzlDockBinEdge">
+ <!-- interface-requires gtk+ 3.24 -->
+ <template class="IdePanel" parent="DzlDockBinEdge">
<child>
<object class="DzlDockStack" id="dock_stack">
<property name="expand">true</property>
@@ -10,3 +10,4 @@
</child>
</template>
</interface>
+
diff --git a/src/libide/preferences/ide-preferences-addin.c b/src/libide/gui/ide-preferences-addin.c
similarity index 67%
rename from src/libide/preferences/ide-preferences-addin.c
rename to src/libide/gui/ide-preferences-addin.c
index cec8ef6af..eef7dd6c2 100644
--- a/src/libide/preferences/ide-preferences-addin.c
+++ b/src/libide/gui/ide-preferences-addin.c
@@ -22,27 +22,13 @@
#include "config.h"
-#include "preferences/ide-preferences-addin.h"
+#include "ide-preferences-addin.h"
G_DEFINE_INTERFACE (IdePreferencesAddin, ide_preferences_addin, G_TYPE_OBJECT)
-static void
-ide_preferences_addin_real_load (IdePreferencesAddin *self,
- DzlPreferences *preferences)
-{
-}
-
-static void
-ide_preferences_addin_real_unload (IdePreferencesAddin *self,
- DzlPreferences *preferences)
-{
-}
-
static void
ide_preferences_addin_default_init (IdePreferencesAddinInterface *iface)
{
- iface->load = ide_preferences_addin_real_load;
- iface->unload = ide_preferences_addin_real_unload;
}
/**
@@ -50,12 +36,13 @@ ide_preferences_addin_default_init (IdePreferencesAddinInterface *iface)
* @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
- * #DzlPreferences interface to add their preferences controls to the container.
+ * This interface method is called when a preferences addin is initialized. It
+ * could be initialized from multiple preferences implementations, so consumers
+ * should use the #DzlPreferences 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.
+ * Such implementations might include a preferences dialog window, or a
+ * preferences widget which could be rendered as a perspective.
*
* Since: 3.32
*/
@@ -66,7 +53,8 @@ ide_preferences_addin_load (IdePreferencesAddin *self,
g_return_if_fail (IDE_IS_PREFERENCES_ADDIN (self));
g_return_if_fail (DZL_IS_PREFERENCES (preferences));
- IDE_PREFERENCES_ADDIN_GET_IFACE (self)->load (self, preferences);
+ if (IDE_PREFERENCES_ADDIN_GET_IFACE (self)->load)
+ IDE_PREFERENCES_ADDIN_GET_IFACE (self)->load (self, preferences);
}
/**
@@ -74,9 +62,9 @@ ide_preferences_addin_load (IdePreferencesAddin *self,
* @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.
+ * 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.
*
* Since: 3.32
*/
@@ -87,5 +75,6 @@ ide_preferences_addin_unload (IdePreferencesAddin *self,
g_return_if_fail (IDE_IS_PREFERENCES_ADDIN (self));
g_return_if_fail (DZL_IS_PREFERENCES (preferences));
- IDE_PREFERENCES_ADDIN_GET_IFACE (self)->unload (self, preferences);
+ if (IDE_PREFERENCES_ADDIN_GET_IFACE (self)->unload)
+ IDE_PREFERENCES_ADDIN_GET_IFACE (self)->unload (self, preferences);
}
diff --git a/src/libide/preferences/ide-preferences-addin.h b/src/libide/gui/ide-preferences-addin.h
similarity index 96%
rename from src/libide/preferences/ide-preferences-addin.h
rename to src/libide/gui/ide-preferences-addin.h
index 5c7d4357a..70fa8f098 100644
--- a/src/libide/preferences/ide-preferences-addin.h
+++ b/src/libide/gui/ide-preferences-addin.h
@@ -21,9 +21,8 @@
#pragma once
#include <dazzle.h>
-#include <gtk/gtk.h>
-#include "ide-version-macros.h"
+#include <libide-core.h>
G_BEGIN_DECLS
diff --git a/src/libide/preferences/ide-preferences-builtin.h
b/src/libide/gui/ide-preferences-builtin-private.h
similarity index 100%
rename from src/libide/preferences/ide-preferences-builtin.h
rename to src/libide/gui/ide-preferences-builtin-private.h
diff --git a/src/libide/preferences/ide-preferences-builtin.c b/src/libide/gui/ide-preferences-builtin.c
similarity index 97%
rename from src/libide/preferences/ide-preferences-builtin.c
rename to src/libide/gui/ide-preferences-builtin.c
index ca2876e22..1fdee4849 100644
--- a/src/libide/preferences/ide-preferences-builtin.c
+++ b/src/libide/gui/ide-preferences-builtin.c
@@ -27,10 +27,8 @@
#include <gtksourceview/gtksource.h>
#include <libpeas/peas.h>
-#include "application/ide-application-private.h"
-#include "preferences/ide-preferences-builtin.h"
-#include "preferences/ide-preferences-language-row.h"
-#include "vcs/ide-vcs-config.h"
+#include "ide-preferences-builtin-private.h"
+#include "ide-preferences-language-row-private.h"
static gint
sort_plugin_info (gconstpointer a,
@@ -142,9 +140,6 @@ ide_preferences_builtin_register_keyboard (DzlPreferences *preferences)
dzl_preferences_add_list_group (preferences, "keyboard", "mode", _("Emulation"), GTK_SELECTION_NONE, 0);
dzl_preferences_add_radio (preferences, "keyboard", "mode", "org.gnome.builder.editor", "keybindings",
NULL, "\"default\"", _("Builder"), _("Default keybinding mode which mimics gedit"), NULL, 0);
- dzl_preferences_add_radio (preferences, "keyboard", "mode", "org.gnome.builder.editor", "keybindings",
NULL, "\"emacs\"", _("Emacs"), _("Emulates the Emacs text editor"), NULL, 0);
- dzl_preferences_add_radio (preferences, "keyboard", "mode", "org.gnome.builder.editor", "keybindings",
NULL, "\"vim\"", _("Vim"), _("Emulates the Vim text editor"), NULL, 0);
- dzl_preferences_add_radio (preferences, "keyboard", "mode", "org.gnome.builder.editor", "keybindings",
NULL, "\"sublime\"", _("Sublime Text"), _("Emulates the Sublime Text editor"), NULL, 0);
dzl_preferences_add_list_group (preferences, "keyboard", "movements", _("Movement"), GTK_SELECTION_NONE,
100);
dzl_preferences_add_switch (preferences, "keyboard", "movements", "org.gnome.builder.editor",
"smart-home-end", NULL, NULL, _("Smart Home and End"), _("Home moves to first non-whitespace character"),
NULL, 0);
@@ -257,13 +252,10 @@ ide_preferences_builtin_register_languages (DzlPreferences *preferences)
{
GtkSourceLanguageManager *manager;
const gchar * const *language_ids;
- g_autoptr(GHashTable) sections = NULL;
GtkSearchEntry *search;
GtkWidget *group = NULL;
GtkWidget *flow = NULL;
- sections = g_hash_table_new (g_str_hash, g_str_equal);
-
dzl_preferences_add_page (preferences, "languages", _("Programming Languages"), 200);
manager = gtk_source_language_manager_get_default ();
@@ -426,6 +418,7 @@ ide_preferences_builtin_register_projects (DzlPreferences *preferences)
dzl_preferences_add_switch (preferences, "projects", "directory", "org.gnome.builder",
"restore-previous-files", NULL, NULL, _("Restore previously opened files"), _("Open previously opened files
when loading a project"), NULL, 10);
}
+#if 0
static void
author_changed_cb (DzlPreferencesEntry *entry,
const gchar *text,
@@ -551,6 +544,7 @@ ide_preferences_builtin_register_vcs (DzlPreferences *preferences)
peas_extension_set_foreach (extensions, vcs_configs_foreach_cb, preferences);
g_clear_object (&extensions);
}
+#endif
static void
ide_preferences_builtin_register_sdks (DzlPreferences *preferences)
@@ -572,6 +566,6 @@ _ide_preferences_builtin_register (DzlPreferences *preferences)
ide_preferences_builtin_register_plugins (preferences);
ide_preferences_builtin_register_build (preferences);
ide_preferences_builtin_register_projects (preferences);
- ide_preferences_builtin_register_vcs (preferences);
+ //ide_preferences_builtin_register_vcs (preferences);
ide_preferences_builtin_register_sdks (preferences);
}
diff --git a/src/libide/preferences/ide-preferences-language-row.h
b/src/libide/gui/ide-preferences-language-row-private.h
similarity index 100%
rename from src/libide/preferences/ide-preferences-language-row.h
rename to src/libide/gui/ide-preferences-language-row-private.h
diff --git a/src/libide/preferences/ide-preferences-language-row.c
b/src/libide/gui/ide-preferences-language-row.c
similarity index 94%
rename from src/libide/preferences/ide-preferences-language-row.c
rename to src/libide/gui/ide-preferences-language-row.c
index 0d2403bf5..b8844d8dc 100644
--- a/src/libide/preferences/ide-preferences-language-row.c
+++ b/src/libide/gui/ide-preferences-language-row.c
@@ -22,7 +22,7 @@
#include "config.h"
-#include "preferences/ide-preferences-language-row.h"
+#include "ide-preferences-language-row-private.h"
struct _IdePreferencesLanguageRow
{
@@ -37,16 +37,16 @@ enum {
PROP_0,
PROP_ID,
PROP_TITLE,
- LAST_PROP
+ N_PROPS
};
enum {
ACTIVATE,
- LAST_SIGNAL
+ N_SIGNALS
};
-static GParamSpec *properties [LAST_PROP];
-static guint signals [LAST_SIGNAL];
+static GParamSpec *properties [N_PROPS];
+static guint signals [N_SIGNALS];
static void
ide_preferences_language_row_activate (IdePreferencesLanguageRow *self)
@@ -148,7 +148,7 @@ ide_preferences_language_row_class_init (IdePreferencesLanguageRowClass *klass)
NULL,
(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
- g_object_class_install_properties (object_class, LAST_PROP, properties);
+ g_object_class_install_properties (object_class, N_PROPS, properties);
signals [ACTIVATE] =
g_signal_new_class_handler ("activate",
@@ -160,7 +160,7 @@ ide_preferences_language_row_class_init (IdePreferencesLanguageRowClass *klass)
widget_class->activate_signal = signals [ACTIVATE];
- gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/builder/ui/ide-preferences-language-row.ui");
+ gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/libide-gui/ui/ide-preferences-language-row.ui");
gtk_widget_class_bind_template_child (widget_class, IdePreferencesLanguageRow, title);
}
diff --git a/src/libide/preferences/ide-preferences-language-row.ui
b/src/libide/gui/ide-preferences-language-row.ui
similarity index 100%
rename from src/libide/preferences/ide-preferences-language-row.ui
rename to src/libide/gui/ide-preferences-language-row.ui
diff --git a/src/libide/gui/ide-preferences-surface.c b/src/libide/gui/ide-preferences-surface.c
new file mode 100644
index 000000000..e132b43c1
--- /dev/null
+++ b/src/libide/gui/ide-preferences-surface.c
@@ -0,0 +1,136 @@
+/* ide-preferences-surface.c
+ *
+ * Copyright 2015-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-preferences-surface"
+
+#include "config.h"
+
+#include <dazzle.h>
+#include <glib/gi18n.h>
+#include <libpeas/peas.h>
+
+#include "ide-preferences-addin.h"
+#include "ide-preferences-builtin-private.h"
+#include "ide-preferences-surface.h"
+#include "ide-surface.h"
+
+struct _IdePreferencesSurface
+{
+ IdeSurface parent_instance;
+ DzlPreferencesView *view;
+ PeasExtensionSet *extensions;
+};
+
+G_DEFINE_TYPE (IdePreferencesSurface, ide_preferences_surface, IDE_TYPE_SURFACE)
+
+static void
+ide_preferences_surface_addin_added_cb (PeasExtensionSet *set,
+ PeasPluginInfo *plugin_info,
+ PeasExtension *extension,
+ gpointer user_data)
+{
+ IdePreferencesSurface *self = user_data;
+
+ g_assert (PEAS_IS_EXTENSION_SET (set));
+ g_assert (plugin_info != NULL);
+ g_assert (IDE_IS_PREFERENCES_ADDIN (extension));
+ g_assert (IDE_IS_PREFERENCES_SURFACE (self));
+
+ ide_preferences_addin_load (IDE_PREFERENCES_ADDIN (extension), DZL_PREFERENCES (self->view));
+ dzl_preferences_view_reapply_filter (self->view);
+}
+
+static void
+ide_preferences_surface_addin_removed_cb (PeasExtensionSet *set,
+ PeasPluginInfo *plugin_info,
+ PeasExtension *extension,
+ gpointer user_data)
+{
+ IdePreferencesSurface *self = user_data;
+
+ g_assert (PEAS_IS_EXTENSION_SET (set));
+ g_assert (plugin_info != NULL);
+ g_assert (IDE_IS_PREFERENCES_ADDIN (extension));
+ g_assert (IDE_IS_PREFERENCES_SURFACE (self));
+
+ ide_preferences_addin_unload (IDE_PREFERENCES_ADDIN (extension), DZL_PREFERENCES (self->view));
+ dzl_preferences_view_reapply_filter (self->view);
+}
+
+static void
+ide_preferences_surface_destroy (GtkWidget *widget)
+{
+ IdePreferencesSurface *self = (IdePreferencesSurface *)widget;
+
+ g_clear_object (&self->extensions);
+
+ GTK_WIDGET_CLASS (ide_preferences_surface_parent_class)->destroy (widget);
+}
+
+static void
+ide_preferences_surface_constructed (GObject *object)
+{
+ IdePreferencesSurface *self = (IdePreferencesSurface *)object;
+
+ G_OBJECT_CLASS (ide_preferences_surface_parent_class)->constructed (object);
+
+ _ide_preferences_builtin_register (DZL_PREFERENCES (self->view));
+
+ self->extensions = peas_extension_set_new (peas_engine_get_default (),
+ IDE_TYPE_PREFERENCES_ADDIN,
+ NULL);
+
+ g_signal_connect (self->extensions,
+ "extension-added",
+ G_CALLBACK (ide_preferences_surface_addin_added_cb),
+ self);
+
+ g_signal_connect (self->extensions,
+ "extension-removed",
+ G_CALLBACK (ide_preferences_surface_addin_removed_cb),
+ self);
+
+ peas_extension_set_foreach (self->extensions,
+ ide_preferences_surface_addin_added_cb,
+ self);
+}
+
+static void
+ide_preferences_surface_class_init (IdePreferencesSurfaceClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->constructed = ide_preferences_surface_constructed;
+
+ widget_class->destroy = ide_preferences_surface_destroy;
+}
+
+static void
+ide_preferences_surface_init (IdePreferencesSurface *self)
+{
+ ide_surface_set_icon_name (IDE_SURFACE (self), "preferences-system-symbolic");
+ gtk_widget_set_name (GTK_WIDGET (self), "preferences");
+
+ self->view = g_object_new (DZL_TYPE_PREFERENCES_VIEW,
+ "visible", TRUE,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (self->view));
+}
diff --git a/src/libide/preferences/ide-preferences-perspective.h b/src/libide/gui/ide-preferences-surface.h
similarity index 69%
rename from src/libide/preferences/ide-preferences-perspective.h
rename to src/libide/gui/ide-preferences-surface.h
index a5bb4e6b3..5657af084 100644
--- a/src/libide/preferences/ide-preferences-perspective.h
+++ b/src/libide/gui/ide-preferences-surface.h
@@ -1,4 +1,4 @@
-/* ide-preferences-perspective.h
+/* ide-preferences-surface.h
*
* Copyright 2015-2019 Christian Hergert <chergert redhat com>
*
@@ -20,16 +20,17 @@
#pragma once
-#include <dazzle.h>
+#if !defined (IDE_GUI_INSIDE) && !defined (IDE_GUI_COMPILATION)
+# error "Only <libide-gui.h> can be included directly."
+#endif
-#include "ide-version-macros.h"
+#include "ide-surface.h"
G_BEGIN_DECLS
-#define IDE_TYPE_PREFERENCES_PERSPECTIVE (ide_preferences_perspective_get_type())
-#define IDE_PREFERENCES_PERSPECTIVE_PRIORITY 1000000
+#define IDE_TYPE_PREFERENCES_SURFACE (ide_preferences_surface_get_type())
IDE_AVAILABLE_IN_3_32
-G_DECLARE_FINAL_TYPE (IdePreferencesPerspective, ide_preferences_perspective, IDE, PREFERENCES_PERSPECTIVE,
DzlPreferencesView)
+G_DECLARE_FINAL_TYPE (IdePreferencesSurface, ide_preferences_surface, IDE, PREFERENCES_SURFACE, IdeSurface)
G_END_DECLS
diff --git a/src/libide/preferences/ide-preferences-window.c b/src/libide/gui/ide-preferences-window.c
similarity index 85%
rename from src/libide/preferences/ide-preferences-window.c
rename to src/libide/gui/ide-preferences-window.c
index bad5151c6..450d5c457 100644
--- a/src/libide/preferences/ide-preferences-window.c
+++ b/src/libide/gui/ide-preferences-window.c
@@ -22,21 +22,21 @@
#include "config.h"
-#include "preferences/ide-preferences-window.h"
+#include "ide-preferences-window.h"
struct _IdePreferencesWindow
{
- GtkWindow parent_instance;
+ DzlApplicationWindow parent_window;
};
-G_DEFINE_TYPE (IdePreferencesWindow, ide_preferences_window, GTK_TYPE_WINDOW)
+G_DEFINE_TYPE (IdePreferencesWindow, ide_preferences_window, DZL_TYPE_APPLICATION_WINDOW)
static void
ide_preferences_window_class_init (IdePreferencesWindowClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
- gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/builder/ui/ide-preferences-window.ui");
+ gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/libide-gui/ui/ide-preferences-window.ui");
}
static void
diff --git a/src/libide/preferences/ide-preferences-window.h b/src/libide/gui/ide-preferences-window.h
similarity index 91%
rename from src/libide/preferences/ide-preferences-window.h
rename to src/libide/gui/ide-preferences-window.h
index d5d9fbb4f..dc7fd2752 100644
--- a/src/libide/preferences/ide-preferences-window.h
+++ b/src/libide/gui/ide-preferences-window.h
@@ -20,15 +20,14 @@
#pragma once
-#include <gtk/gtk.h>
-
-#include "ide-version-macros.h"
+#include <dazzle.h>
+#include <libide-core.h>
G_BEGIN_DECLS
#define IDE_TYPE_PREFERENCES_WINDOW (ide_preferences_window_get_type())
IDE_AVAILABLE_IN_3_32
-G_DECLARE_FINAL_TYPE (IdePreferencesWindow, ide_preferences_window, IDE, PREFERENCES_WINDOW, GtkWindow)
+G_DECLARE_FINAL_TYPE (IdePreferencesWindow, ide_preferences_window, IDE, PREFERENCES_WINDOW,
DzlApplicationWindow)
G_END_DECLS
diff --git a/src/libide/gui/ide-preferences-window.ui b/src/libide/gui/ide-preferences-window.ui
new file mode 100644
index 000000000..a02207197
--- /dev/null
+++ b/src/libide/gui/ide-preferences-window.ui
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="IdePreferencesWindow" parent="DzlApplicationWindow">
+ <child type="titlebar">
+ <object class="IdeHeaderBar" id="header_bar">
+ <property name="title" translatable="yes">Preferences</property>
+ <property name="show-close-button">true</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ <child>
+ <object class="IdePreferencesSurface" id="surface">
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/src/libide/gui/ide-primary-workspace-actions.c b/src/libide/gui/ide-primary-workspace-actions.c
new file mode 100644
index 000000000..e90976f4e
--- /dev/null
+++ b/src/libide/gui/ide-primary-workspace-actions.c
@@ -0,0 +1,109 @@
+/* ide-primary-workspace-actions.c
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-primary-workspace-actions"
+
+#include "config.h"
+
+#include <libide-foundry.h>
+#include <libpeas/peas.h>
+
+#include "ide-gui-global.h"
+#include "ide-gui-private.h"
+#include "ide-primary-workspace.h"
+
+static void
+update_dependencies_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeDependencyUpdater *updater = (IdeDependencyUpdater *)object;
+ g_autoptr(IdePrimaryWorkspace) self = user_data;
+ g_autoptr(GError) error = NULL;
+ IdeContext *context;
+
+ g_assert (IDE_IS_DEPENDENCY_UPDATER (updater));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_PRIMARY_WORKSPACE (self));
+
+ context = ide_widget_get_context (GTK_WIDGET (self));
+
+ if (!ide_dependency_updater_update_finish (updater, result, &error))
+ ide_context_warning (context, "%s", error->message);
+
+ ide_object_destroy (IDE_OBJECT (updater));
+}
+
+static void
+ide_primary_workspace_actions_update_dependencies_cb (PeasExtensionSet *set,
+ PeasPluginInfo *plugin_info,
+ PeasExtension *exten,
+ gpointer user_data)
+{
+ IdeDependencyUpdater *updater = (IdeDependencyUpdater *)exten;
+ IdePrimaryWorkspace *self = user_data;
+ IdeContext *context;
+
+ g_assert (PEAS_IS_EXTENSION_SET (set));
+ g_assert (plugin_info != NULL);
+ g_assert (IDE_IS_DEPENDENCY_UPDATER (updater));
+ g_assert (IDE_IS_PRIMARY_WORKSPACE (self));
+
+ context = ide_widget_get_context (GTK_WIDGET (self));
+ ide_object_append (IDE_OBJECT (context), IDE_OBJECT (updater));
+
+ ide_dependency_updater_update_async (updater,
+ NULL,
+ update_dependencies_cb,
+ g_object_ref (self));
+}
+
+static void
+ide_primary_workspace_actions_update_dependencies (GSimpleAction *action,
+ GVariant *param,
+ gpointer user_data)
+{
+ IdePrimaryWorkspace *self = user_data;
+ g_autoptr(PeasExtensionSet) set = NULL;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (G_IS_SIMPLE_ACTION (action));
+ g_assert (IDE_IS_PRIMARY_WORKSPACE (self));
+
+ set = peas_extension_set_new (peas_engine_get_default (),
+ IDE_TYPE_DEPENDENCY_UPDATER,
+ NULL);
+ peas_extension_set_foreach (set, ide_primary_workspace_actions_update_dependencies_cb, self);
+}
+
+static const GActionEntry actions[] = {
+ { "update-dependencies", ide_primary_workspace_actions_update_dependencies },
+};
+
+void
+_ide_primary_workspace_init_actions (IdePrimaryWorkspace *self)
+{
+ g_assert (IDE_IS_PRIMARY_WORKSPACE (self));
+
+ g_action_map_add_action_entries (G_ACTION_MAP (self),
+ actions,
+ G_N_ELEMENTS (actions),
+ self);
+}
diff --git a/src/libide/gui/ide-primary-workspace.c b/src/libide/gui/ide-primary-workspace.c
new file mode 100644
index 000000000..a88b3d9fd
--- /dev/null
+++ b/src/libide/gui/ide-primary-workspace.c
@@ -0,0 +1,141 @@
+/* ide-primary-workspace.c
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-primary-workspace"
+
+#include "config.h"
+
+#include "ide-gui-global.h"
+#include "ide-gui-private.h"
+#include "ide-header-bar.h"
+#include "ide-omni-bar.h"
+#include "ide-primary-workspace.h"
+#include "ide-run-button.h"
+#include "ide-search-entry.h"
+#include "ide-surface.h"
+#include "ide-window-settings-private.h"
+
+/**
+ * SECTION:ide-primary-workspace
+ * @title: IdePrimaryWorkspace
+ * @short_description: The primary IDE window
+ *
+ * The primary workspace is the main workspace window for the user. This is the
+ * "IDE experience" workspace. It is generally created by the workbench when
+ * opening a project (unless another workspace type has been requested).
+ *
+ * See ide_workbench_open_async() for how to select another workspace type
+ * when opening a project.
+ *
+ * Returns: (transfer full): an #IdePrimaryWorkspace
+ *
+ * Since: 3.32
+ */
+
+struct _IdePrimaryWorkspace
+{
+ IdeWorkspace parent_instance;
+
+ /* Template widgets */
+ IdeHeaderBar *header_bar;
+ DzlMenuButton *surface_menu_button;
+ IdeRunButton *run_button;
+ IdeSearchEntry *search_entry;
+ GtkLabel *project_title;
+};
+
+G_DEFINE_TYPE (IdePrimaryWorkspace, ide_primary_workspace, IDE_TYPE_WORKSPACE)
+
+static void
+ide_primary_workspace_context_set (IdeWorkspace *workspace,
+ IdeContext *context)
+{
+ IdePrimaryWorkspace *self = (IdePrimaryWorkspace *)workspace;
+ IdeProjectInfo *project_info;
+ IdeWorkbench *workbench;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_PRIMARY_WORKSPACE (self));
+ g_assert (IDE_IS_CONTEXT (context));
+
+ IDE_WORKSPACE_CLASS (ide_primary_workspace_parent_class)->context_set (workspace, context);
+
+ workbench = ide_widget_get_workbench (GTK_WIDGET (self));
+ project_info = ide_workbench_get_project_info (workbench);
+
+ if (project_info)
+ g_object_bind_property (project_info, "name", self->project_title, "label",
+ G_BINDING_SYNC_CREATE);
+}
+
+static void
+ide_primary_workspace_surface_set (IdeWorkspace *workspace,
+ IdeSurface *surface)
+{
+ IdePrimaryWorkspace *self = (IdePrimaryWorkspace *)workspace;
+
+ g_assert (IDE_IS_PRIMARY_WORKSPACE (self));
+ g_assert (!surface || IDE_IS_SURFACE (surface));
+
+ if (DZL_IS_DOCK_ITEM (surface))
+ {
+ g_autofree gchar *icon_name = NULL;
+
+ icon_name = dzl_dock_item_get_icon_name (DZL_DOCK_ITEM (surface));
+ g_object_set (self->surface_menu_button,
+ "icon-name", icon_name,
+ NULL);
+ }
+
+ IDE_WORKSPACE_CLASS (ide_primary_workspace_parent_class)->surface_set (workspace, surface);
+}
+
+static void
+ide_primary_workspace_class_init (IdePrimaryWorkspaceClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ IdeWorkspaceClass *workspace_class = IDE_WORKSPACE_CLASS (klass);
+
+ ide_workspace_class_set_kind (workspace_class, "primary");
+
+ workspace_class->surface_set = ide_primary_workspace_surface_set;
+ workspace_class->context_set = ide_primary_workspace_context_set;
+
+ gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/libide-gui/ui/ide-primary-workspace.ui");
+ gtk_widget_class_bind_template_child (widget_class, IdePrimaryWorkspace, header_bar);
+ gtk_widget_class_bind_template_child (widget_class, IdePrimaryWorkspace, project_title);
+ gtk_widget_class_bind_template_child (widget_class, IdePrimaryWorkspace, run_button);
+ gtk_widget_class_bind_template_child (widget_class, IdePrimaryWorkspace, search_entry);
+ gtk_widget_class_bind_template_child (widget_class, IdePrimaryWorkspace, surface_menu_button);
+
+ g_type_ensure (IDE_TYPE_HEADER_BAR);
+ g_type_ensure (IDE_TYPE_OMNI_BAR);
+ g_type_ensure (IDE_TYPE_RUN_BUTTON);
+ g_type_ensure (IDE_TYPE_SEARCH_ENTRY);
+}
+
+static void
+ide_primary_workspace_init (IdePrimaryWorkspace *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ _ide_primary_workspace_init_actions (self);
+ _ide_window_settings_register (GTK_WINDOW (self));
+}
diff --git a/src/libide/gui/ide-primary-workspace.h b/src/libide/gui/ide-primary-workspace.h
new file mode 100644
index 000000000..a20da72eb
--- /dev/null
+++ b/src/libide/gui/ide-primary-workspace.h
@@ -0,0 +1,38 @@
+/* ide-primary-workspace.h
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#if !defined (IDE_GUI_INSIDE) && !defined (IDE_GUI_COMPILATION)
+# error "Only <libide-gui.h> can be included directly."
+#endif
+
+#include "ide-application.h"
+#include "ide-surface.h"
+#include "ide-workspace.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_PRIMARY_WORKSPACE (ide_primary_workspace_get_type())
+
+IDE_AVAILABLE_IN_3_32
+G_DECLARE_FINAL_TYPE (IdePrimaryWorkspace, ide_primary_workspace, IDE, PRIMARY_WORKSPACE, IdeWorkspace)
+
+G_END_DECLS
diff --git a/src/libide/gui/ide-primary-workspace.ui b/src/libide/gui/ide-primary-workspace.ui
new file mode 100644
index 000000000..a374869b0
--- /dev/null
+++ b/src/libide/gui/ide-primary-workspace.ui
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="IdePrimaryWorkspace" parent="IdeWorkspace">
+ <child type="titlebar">
+ <object class="IdeHeaderBar" id="header_bar">
+ <property name="menu-id">ide-primary-workspace-menu</property>
+ <property name="show-close-button">true</property>
+ <property name="show-fullscreen-button">true</property>
+ <property name="visible">true</property>
+ <child type="left">
+ <object class="IdeSurfacesButton" id="surface_menu_button">
+ <property name="focus-on-click">false</property>
+ <property name="menu-id">ide-primary-workspace-surfaces-menu</property>
+ <property name="show-accels">true</property>
+ <property name="show-arrow">true</property>
+ <property name="show-icons">true</property>
+ <!-- disable transitions since they'll cause jitter with the
+ whole surface changing below it. -->
+ <property name="transitions-enabled">false</property>
+ <property name="has-tooltip">true</property>
+ <property name="tooltip-text" translatable="yes">Change workspace surface</property>
+ </object>
+ </child>
+ <child type="title">
+ <object class="IdeOmniBar" id="omni_bar">
+ <property name="halign">center</property>
+ <property name="hexpand">false</property>
+ <property name="hexpand-set">true</property>
+ <property name="visible">true</property>
+ <child type="placeholder">
+ <object class="GtkLabel" id="project_title">
+ <property name="visible">true</property>
+ <property name="xalign">0.0</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="right-of-center">
+ <object class="IdeRunButton" id="run_button">
+ <property name="visible">true</property>
+ </object>
+ </child>
+ <child type="right">
+ <object class="IdeNotificationsButton" id="notifications_button">
+ <property name="visible">true</property>
+ </object>
+ </child>
+ <child type="right">
+ <object class="IdeSearchEntry" id="search_entry">
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
+ <property name="primary-icon-name">edit-find-symbolic</property>
+ <property name="placeholder-text" translatable="yes">Press Ctrl+. to search</property>
+ <property name="width-chars">5</property>
+ <property name="max-width-chars">24</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/src/libide/gui/ide-run-button.c b/src/libide/gui/ide-run-button.c
new file mode 100644
index 000000000..6b69341ba
--- /dev/null
+++ b/src/libide/gui/ide-run-button.c
@@ -0,0 +1,200 @@
+/* ide-run-button.c
+ *
+ * Copyright 2016-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-run-button"
+
+#include "config.h"
+
+#include <dazzle.h>
+#include <glib/gi18n.h>
+#include <libide-foundry.h>
+
+#include "ide-gui-global.h"
+#include "ide-run-button.h"
+
+#include "ide-run-manager-private.h"
+
+struct _IdeRunButton
+{
+ GtkBox parent_instance;
+
+ GtkButton *button;
+ GtkImage *button_image;
+ DzlMenuButton *menu_button;
+ GtkButton *stop_button;
+ GtkShortcutsShortcut *run_shortcut;
+ GtkLabel *run_tooltip_message;
+ DzlShortcutTooltip *tooltip;
+};
+
+G_DEFINE_TYPE (IdeRunButton, ide_run_button, GTK_TYPE_BOX)
+
+static void
+ide_run_button_handler_set (IdeRunButton *self,
+ GParamSpec *pspec,
+ IdeRunManager *run_manager)
+{
+ const GList *list;
+ const GList *iter;
+ const gchar *handler;
+
+ g_assert (IDE_IS_RUN_BUTTON (self));
+ g_assert (IDE_IS_RUN_MANAGER (run_manager));
+
+ handler = ide_run_manager_get_handler (run_manager);
+ list = _ide_run_manager_get_handlers (run_manager);
+
+ for (iter = list; iter; iter = iter->next)
+ {
+ const IdeRunHandlerInfo *info = iter->data;
+
+ if (g_strcmp0 (info->id, handler) == 0)
+ {
+ g_object_set (self->button_image,
+ "icon-name", info->icon_name,
+ NULL);
+ break;
+ }
+ }
+}
+
+static void
+ide_run_button_load (IdeRunButton *self,
+ IdeContext *context)
+{
+ IdeRunManager *run_manager;
+
+ g_assert (IDE_IS_RUN_BUTTON (self));
+ g_assert (IDE_IS_CONTEXT (context));
+
+ run_manager = ide_run_manager_from_context (context);
+
+ g_object_bind_property (run_manager, "busy", self->button, "visible",
+ G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
+ g_object_bind_property (run_manager, "busy", self->stop_button, "visible",
+ G_BINDING_SYNC_CREATE);
+
+ g_signal_connect_object (run_manager,
+ "notify::handler",
+ G_CALLBACK (ide_run_button_handler_set),
+ self,
+ G_CONNECT_SWAPPED);
+
+ ide_run_button_handler_set (self, NULL, run_manager);
+}
+
+static void
+ide_run_button_context_set (GtkWidget *widget,
+ IdeContext *context)
+{
+ IdeRunButton *self = (IdeRunButton *)widget;
+
+ g_assert (IDE_IS_RUN_BUTTON (self));
+ g_assert (!context || IDE_IS_CONTEXT (context));
+
+ if (context != NULL)
+ ide_run_button_load (self, context);
+}
+
+static gboolean
+ide_run_button_query_tooltip (IdeRunButton *self,
+ gint x,
+ gint y,
+ gboolean keyboard_tooltip,
+ GtkTooltip *tooltip,
+ GtkButton *button)
+{
+ IdeRunManager *run_manager;
+ const GList *list;
+ const GList *iter;
+ const gchar *handler;
+ IdeContext *context;
+
+ g_assert (IDE_IS_RUN_BUTTON (self));
+ g_assert (GTK_IS_TOOLTIP (tooltip));
+ g_assert (GTK_IS_BUTTON (button));
+
+ context = ide_widget_get_context (GTK_WIDGET (self));
+ run_manager = ide_run_manager_from_context (context);
+ handler = ide_run_manager_get_handler (run_manager);
+ list = _ide_run_manager_get_handlers (run_manager);
+
+ for (iter = list; iter; iter = iter->next)
+ {
+ const IdeRunHandlerInfo *info = iter->data;
+
+ if (g_strcmp0 (info->id, handler) == 0)
+ {
+ gboolean enabled;
+ /* Figure out if the run action is enabled. If it
+ * is not, then we should inform the user that
+ * the project cannot be run yet because the
+ * build pipeline is not yet configured. */
+ g_action_group_query_action (G_ACTION_GROUP (run_manager),
+ "run",
+ &enabled,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ if (!enabled)
+ {
+ gtk_tooltip_set_custom (tooltip, GTK_WIDGET (self->run_tooltip_message));
+ return TRUE;
+ }
+
+ /* The shortcut tooltip will set this up after us */
+ dzl_shortcut_tooltip_set_accel (self->tooltip, info->accel);
+ dzl_shortcut_tooltip_set_title (self->tooltip, info->title);
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+ide_run_button_class_init (IdeRunButtonClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/libide-gui/ui/ide-run-button.ui");
+ gtk_widget_class_bind_template_child (widget_class, IdeRunButton, button);
+ gtk_widget_class_bind_template_child (widget_class, IdeRunButton, button_image);
+ gtk_widget_class_bind_template_child (widget_class, IdeRunButton, menu_button);
+ gtk_widget_class_bind_template_child (widget_class, IdeRunButton, run_shortcut);
+ gtk_widget_class_bind_template_child (widget_class, IdeRunButton, stop_button);
+ gtk_widget_class_bind_template_child (widget_class, IdeRunButton, run_tooltip_message);
+ gtk_widget_class_bind_template_child (widget_class, IdeRunButton, tooltip);
+}
+
+static void
+ide_run_button_init (IdeRunButton *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ g_signal_connect_object (self->button,
+ "query-tooltip",
+ G_CALLBACK (ide_run_button_query_tooltip),
+ self,
+ G_CONNECT_SWAPPED);
+
+ ide_widget_set_context_handler (self, ide_run_button_context_set);
+}
diff --git a/src/libide/util/ide-dnd.h b/src/libide/gui/ide-run-button.h
similarity index 75%
rename from src/libide/util/ide-dnd.h
rename to src/libide/gui/ide-run-button.h
index 03d636641..f758c3adc 100644
--- a/src/libide/util/ide-dnd.h
+++ b/src/libide/gui/ide-run-button.h
@@ -1,6 +1,6 @@
-/* ide-dnd.h
+/* ide-run-button.h
*
- * Copyright 2015 Dimitris Zenios <dimitris zenios gmail com>
+ * Copyright 2016-2019 Christian Hergert <chergert redhat com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -24,6 +24,10 @@
G_BEGIN_DECLS
-gchar **ide_dnd_get_uri_list (GtkSelectionData *selection_data);
+#define IDE_TYPE_RUN_BUTTON (ide_run_button_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeRunButton, ide_run_button, IDE, RUN_BUTTON, GtkBox)
+
+GtkWidget *ide_run_button_new (void);
G_END_DECLS
diff --git a/src/libide/gui/ide-run-button.ui b/src/libide/gui/ide-run-button.ui
new file mode 100644
index 000000000..e68678402
--- /dev/null
+++ b/src/libide/gui/ide-run-button.ui
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <object class="GtkShortcutsShortcut" id="run_shortcut">
+ </object>
+ <object class="GtkLabel" id="run_tooltip_message">
+ <property name="label" translatable="yes">The project cannot be run while the build pipeline is being
set up</property>
+ <property name="visible">true</property>
+ </object>
+ <template class="IdeRunButton" parent="GtkBox">
+ <property name="orientation">horizontal</property>
+ <style>
+ <class name="linked"/>
+ </style>
+ <child>
+ <object class="GtkButton" id="button">
+ <property name="action-name">run-manager.run</property>
+ <property name="focus-on-click">false</property>
+ <property name="has-tooltip">true</property>
+ <property name="visible">true</property>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child>
+ <object class="GtkImage" id="button_image">
+ <property name="icon-name">media-playback-start-symbolic</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="stop_button">
+ <property name="action-name">run-manager.stop</property>
+ <property name="focus-on-click">false</property>
+ <property name="tooltip-text" translatable="yes">Stop running</property>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="icon-name">media-playback-stop-symbolic</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="DzlMenuButton" id="menu_button">
+ <property name="focus-on-click">false</property>
+ <property name="icon-name">pan-down-symbolic</property>
+ <property name="menu-id">run-menu</property>
+ <property name="show-accels">true</property>
+ <property name="show-arrow">false</property>
+ <property name="show-icons">true</property>
+ <property name="tooltip-text" translatable="yes">Change run options</property>
+ <property name="visible">true</property>
+ <style>
+ <class name="image-button"/>
+ <class name="run-arrow-button"/>
+ </style>
+ </object>
+ </child>
+ </template>
+ <object class="DzlShortcutTooltip" id="tooltip">
+ <property name="widget">button</property>
+ </object>
+</interface>
diff --git a/src/libide/search/ide-search-entry.c b/src/libide/gui/ide-search-entry.c
similarity index 65%
rename from src/libide/search/ide-search-entry.c
rename to src/libide/gui/ide-search-entry.c
index 9632cb4d3..8d8088ca4 100644
--- a/src/libide/search/ide-search-entry.c
+++ b/src/libide/gui/ide-search-entry.c
@@ -22,29 +22,23 @@
#include "config.h"
-#include "ide-context.h"
+#include <glib/gi18n.h>
+#include <libide-core.h>
+#include <libide-search.h>
-#include "editor/ide-editor-perspective.h"
-#include "search/ide-search-engine.h"
-#include "search/ide-search-entry.h"
-#include "search/ide-search-result.h"
-#include "util/ide-gtk.h"
-#include "workbench/ide-workbench.h"
+#include "ide-gui-global.h"
+#include "ide-search-entry.h"
+#include "ide-workbench.h"
#define DEFAULT_SEARCH_MAX 25
+#define I_ g_intern_string
struct _IdeSearchEntry
{
DzlSuggestionEntry parent_instance;
- guint max_results;
+ guint max_results;
};
-typedef struct
-{
- IdeEditorPerspective *editor;
- IdeSourceLocation *location;
-} DelayedActivate;
-
G_DEFINE_TYPE (IdeSearchEntry, ide_search_entry, DZL_TYPE_SUGGESTION_ENTRY)
enum {
@@ -61,6 +55,35 @@ enum {
static GParamSpec *properties [N_PROPS];
static guint signals [N_SIGNALS];
+static void
+search_popover_position_func (DzlSuggestionEntry *entry,
+ GdkRectangle *area,
+ gboolean *is_absolute,
+ gpointer user_data)
+{
+ gint new_width;
+
+ g_assert (DZL_IS_SUGGESTION_ENTRY (entry));
+ g_assert (area != NULL);
+ g_assert (is_absolute != NULL);
+ g_assert (user_data == NULL);
+
+#define RIGHT_MARGIN 6
+
+ /* We want the search area to be the right 2/5ths of the window, with a bit
+ * of margin on the popover.
+ */
+
+ dzl_suggestion_entry_window_position_func (entry, area, is_absolute, NULL);
+
+ new_width = (area->width * 2 / 5);
+ area->x += area->width - new_width;
+ area->width = new_width - RIGHT_MARGIN;
+ area->y -= 3;
+
+#undef RIGHT_MARGIN
+}
+
static void
ide_search_entry_search_cb (GObject *object,
GAsyncResult *result,
@@ -94,93 +117,35 @@ static void
ide_search_entry_changed (IdeSearchEntry *self)
{
IdeSearchEngine *engine;
- IdeContext *context;
+ IdeWorkbench *workbench;
const gchar *typed_text;
g_assert (IDE_IS_SEARCH_ENTRY (self));
- if (NULL == (context = ide_widget_get_context (GTK_WIDGET (self))))
- return;
-
+ workbench = ide_widget_get_workbench (GTK_WIDGET (self));
+ engine = ide_workbench_get_search_engine (workbench);
typed_text = dzl_suggestion_entry_get_typed_text (DZL_SUGGESTION_ENTRY (self));
if (dzl_str_empty0 (typed_text))
- {
- dzl_suggestion_entry_set_model (DZL_SUGGESTION_ENTRY (self), NULL);
- return;
- }
-
- engine = ide_context_get_search_engine (context);
-
- ide_search_engine_search_async (engine,
- typed_text,
- self->max_results,
- NULL,
- ide_search_entry_search_cb,
- g_object_ref (self));
-}
-
-static void
-delayed_activate_free (gpointer data)
-{
- DelayedActivate *da = data;
-
- g_clear_object (&da->editor);
- g_clear_pointer (&da->location, ide_source_location_unref);
- g_slice_free (DelayedActivate, da);
-}
-
-static gboolean
-delayed_activate_handle (gpointer data)
-{
- DelayedActivate *da = data;
-
- g_assert (da != NULL);
- g_assert (IDE_IS_EDITOR_PERSPECTIVE (da->editor));
- g_assert (da->location != NULL);
-
- ide_editor_perspective_focus_location (da->editor, da->location);
-
- return G_SOURCE_REMOVE;
+ dzl_suggestion_entry_set_model (DZL_SUGGESTION_ENTRY (self), NULL);
+ else
+ ide_search_engine_search_async (engine,
+ typed_text,
+ self->max_results,
+ NULL,
+ ide_search_entry_search_cb,
+ g_object_ref (self));
}
static void
suggestion_activated (DzlSuggestionEntry *entry,
DzlSuggestion *suggestion)
{
- g_autoptr(IdeSourceLocation) location = NULL;
-
g_assert (IDE_IS_SEARCH_ENTRY (entry));
g_assert (IDE_IS_SEARCH_RESULT (suggestion));
- location = ide_search_result_get_source_location (IDE_SEARCH_RESULT (suggestion));
-
- if (location != NULL)
- {
- IdeWorkbench *workbench;
- IdePerspective *perspective;
- DelayedActivate *da;
-
- workbench = ide_widget_get_workbench (GTK_WIDGET (entry));
- perspective = ide_workbench_get_perspective_by_name (workbench, "editor");
-
- /*
- * The goal here is to wait a short bit of time before activating the
- * item so that our window has a chance to animate out. Otherwise, we
- * get a jittery animation because the UI is likely doing too much work
- * (such as creating and sink'ing the new widgetry).
- */
-
- da = g_slice_new0 (DelayedActivate);
- da->editor = g_object_ref (IDE_EDITOR_PERSPECTIVE (perspective));
- da->location = g_steal_pointer (&location);
-
- gdk_threads_add_timeout_full (G_PRIORITY_LOW,
- 250,
- delayed_activate_handle,
- g_steal_pointer (&da),
- delayed_activate_free);
- }
+ /* TODO: Get last focus from workspace */
+ ide_search_result_activate (IDE_SEARCH_RESULT (suggestion), GTK_WIDGET (entry));
/* Chain up to properly clear entry buffer */
if (DZL_SUGGESTION_ENTRY_CLASS (ide_search_entry_parent_class)->suggestion_activated)
@@ -238,6 +203,37 @@ ide_search_entry_set_property (GObject *object,
}
}
+static DzlShortcutEntry shortcuts[] = {
+ { "org.gnome.builder.workspace.global-search",
+ 0, NULL,
+ NC_("shortcut window", "Workspace shortcuts"),
+ NC_("shortcut window", "Search"),
+ NC_("shortcut window", "Focus to the global search entry") },
+};
+
+static void
+ide_search_entry_init_shortcuts (IdeSearchEntry *self)
+{
+ DzlShortcutController *controller;
+
+ g_assert (IDE_IS_SEARCH_ENTRY (self));
+
+ controller = dzl_shortcut_controller_find (GTK_WIDGET (self));
+
+ dzl_shortcut_controller_add_command_callback (controller,
+ I_("org.gnome.builder.workspace.global-search"),
+ "<Primary>period",
+ DZL_SHORTCUT_PHASE_CAPTURE | DZL_SHORTCUT_PHASE_GLOBAL,
+ (GtkCallback)gtk_widget_grab_focus,
+ NULL,
+ NULL);
+
+ dzl_shortcut_manager_add_shortcut_entries (NULL,
+ shortcuts,
+ G_N_ELEMENTS (shortcuts),
+ GETTEXT_PACKAGE);
+}
+
static void
ide_search_entry_class_init (IdeSearchEntryClass *klass)
{
@@ -269,7 +265,7 @@ ide_search_entry_class_init (IdeSearchEntryClass *klass)
G_CALLBACK (ide_search_entry_unfocus),
NULL, NULL, NULL, G_TYPE_NONE, 0);
- gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/builder/ui/ide-search-entry.ui");
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/libide-gui/ui/ide-search-entry.ui");
bindings = gtk_binding_set_by_class (klass);
gtk_binding_entry_add_signal (bindings, GDK_KEY_Escape, 0, "unfocus", 0);
@@ -288,4 +284,11 @@ ide_search_entry_init (IdeSearchEntry *self)
"changed",
G_CALLBACK (ide_search_entry_changed),
NULL);
+
+ dzl_suggestion_entry_set_position_func (DZL_SUGGESTION_ENTRY (self),
+ search_popover_position_func,
+ NULL,
+ NULL);
+
+ ide_search_entry_init_shortcuts (self);
}
diff --git a/src/libide/search/ide-search-entry.h b/src/libide/gui/ide-search-entry.h
similarity index 89%
rename from src/libide/search/ide-search-entry.h
rename to src/libide/gui/ide-search-entry.h
index 70d4d3aac..f1bd206ea 100644
--- a/src/libide/search/ide-search-entry.h
+++ b/src/libide/gui/ide-search-entry.h
@@ -20,9 +20,11 @@
#pragma once
-#include <dazzle.h>
+#if !defined (IDE_GUI_INSIDE) && !defined (IDE_GUI_COMPILATION)
+# error "Only <libide-gui.h> can be included directly."
+#endif
-#include "ide-version-macros.h"
+#include <dazzle.h>
G_BEGIN_DECLS
diff --git a/src/libide/search/ide-search-entry.ui b/src/libide/gui/ide-search-entry.ui
similarity index 84%
rename from src/libide/search/ide-search-entry.ui
rename to src/libide/gui/ide-search-entry.ui
index c5650d24e..f5e623ca1 100644
--- a/src/libide/search/ide-search-entry.ui
+++ b/src/libide/gui/ide-search-entry.ui
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="IdeSearchEntry" parent="DzlSuggestionEntry">
- <property name="primary-icon-name">edit-find-symbolic</property>
<child internal-child="popover">
<object class="DzlSuggestionPopover">
<property name="title-ellipsize">middle</property>
diff --git a/src/libide/session/ide-session-addin.c b/src/libide/gui/ide-session-addin.c
similarity index 88%
rename from src/libide/session/ide-session-addin.c
rename to src/libide/gui/ide-session-addin.c
index da00f7824..576e91023 100644
--- a/src/libide/session/ide-session-addin.c
+++ b/src/libide/gui/ide-session-addin.c
@@ -28,6 +28,7 @@ G_DEFINE_INTERFACE (IdeSessionAddin, ide_session_addin, IDE_TYPE_OBJECT)
static void
ide_session_addin_real_save_async (IdeSessionAddin *self,
+ IdeWorkbench *workbench,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
@@ -49,6 +50,7 @@ ide_session_addin_real_save_finish (IdeSessionAddin *self,
static void
ide_session_addin_real_restore_async (IdeSessionAddin *self,
+ IdeWorkbench *workbench,
GVariant *state,
GCancellable *cancellable,
GAsyncReadyCallback callback,
@@ -81,6 +83,7 @@ ide_session_addin_default_init (IdeSessionAddinInterface *iface)
/**
* ide_session_addin_save_async:
* @self: a #IdeSessionAddin
+ * @workbench: an #IdeWorkbench
* @cancellable: (nullable): A #GCancellable or %NULL
* @callback: callback to execute upon completion
* @user_data: closure data for @callback
@@ -90,18 +93,20 @@ ide_session_addin_default_init (IdeSessionAddinInterface *iface)
* The resulting state will be provided when restoring the addin
* at a future time.
*
- * Since: 3.32
+ * Since: 3.30
*/
void
ide_session_addin_save_async (IdeSessionAddin *self,
+ IdeWorkbench *workbench,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IDE_IS_SESSION_ADDIN (self));
+ g_return_if_fail (IDE_IS_WORKBENCH (workbench));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
- IDE_SESSION_ADDIN_GET_IFACE (self)->save_async (self, cancellable, callback, user_data);
+ IDE_SESSION_ADDIN_GET_IFACE (self)->save_async (self, workbench, cancellable, callback, user_data);
}
/**
@@ -114,7 +119,7 @@ ide_session_addin_save_async (IdeSessionAddin *self,
*
* Returns: (transfer full) (nullable): a #GVariant or %NULL.
*
- * Since: 3.32
+ * Since: 3.30
*/
GVariant *
ide_session_addin_save_finish (IdeSessionAddin *self,
@@ -130,6 +135,7 @@ ide_session_addin_save_finish (IdeSessionAddin *self,
/**
* ide_session_addin_restore_async:
* @self: a #IdeSessionAddin
+ * @workbench: an #IdeWorkbench
* @state: a #GVariant of previous state
* @cancellable: (nullable): A #GCancellable or %NULL
* @callback: callback to execute upon completion
@@ -137,19 +143,21 @@ ide_session_addin_save_finish (IdeSessionAddin *self,
*
* Asynchronous request to restore session state by the addin.
*
- * Since: 3.32
+ * Since: 3.30
*/
void
ide_session_addin_restore_async (IdeSessionAddin *self,
+ IdeWorkbench *workbench,
GVariant *state,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IDE_IS_SESSION_ADDIN (self));
+ g_return_if_fail (IDE_IS_WORKBENCH (workbench));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
- IDE_SESSION_ADDIN_GET_IFACE (self)->restore_async (self, state, cancellable, callback, user_data);
+ IDE_SESSION_ADDIN_GET_IFACE (self)->restore_async (self, workbench, state, cancellable, callback,
user_data);
}
gboolean
diff --git a/src/libide/session/ide-session-addin.h b/src/libide/gui/ide-session-addin.h
similarity index 87%
rename from src/libide/session/ide-session-addin.h
rename to src/libide/gui/ide-session-addin.h
index 687944391..14425f5e3 100644
--- a/src/libide/session/ide-session-addin.h
+++ b/src/libide/gui/ide-session-addin.h
@@ -20,8 +20,13 @@
#pragma once
-#include "ide-object.h"
-#include "ide-version-macros.h"
+#if !defined (IDE_GUI_INSIDE) && !defined (IDE_GUI_COMPILATION)
+# error "Only <libide-gui> can be included directly."
+#endif
+
+#include <libide-core.h>
+
+#include "ide-workbench.h"
G_BEGIN_DECLS
@@ -35,6 +40,7 @@ struct _IdeSessionAddinInterface
GTypeInterface parent;
void (*save_async) (IdeSessionAddin *self,
+ IdeWorkbench *workbench,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
@@ -42,6 +48,7 @@ struct _IdeSessionAddinInterface
GAsyncResult *result,
GError **error);
void (*restore_async) (IdeSessionAddin *self,
+ IdeWorkbench *workbench,
GVariant *state,
GCancellable *cancellable,
GAsyncReadyCallback callback,
@@ -53,6 +60,7 @@ struct _IdeSessionAddinInterface
IDE_AVAILABLE_IN_3_32
void ide_session_addin_save_async (IdeSessionAddin *self,
+ IdeWorkbench *workbench,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
@@ -62,6 +70,7 @@ GVariant *ide_session_addin_save_finish (IdeSessionAddin *self,
GError **error);
IDE_AVAILABLE_IN_3_32
void ide_session_addin_restore_async (IdeSessionAddin *self,
+ IdeWorkbench *workbench,
GVariant *state,
GCancellable *cancellable,
GAsyncReadyCallback callback,
diff --git a/src/libide/gui/ide-session-private.h b/src/libide/gui/ide-session-private.h
new file mode 100644
index 000000000..7a7f343dc
--- /dev/null
+++ b/src/libide/gui/ide-session-private.h
@@ -0,0 +1,51 @@
+/* ide-session-private.h
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <libide-core.h>
+
+#include "ide-workbench.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_SESSION (ide_session_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeSession, ide_session, IDE, SESSION, IdeObject)
+
+IdeSession *ide_session_new (void);
+void ide_session_restore_async (IdeSession *self,
+ IdeWorkbench *workbench,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean ide_session_restore_finish (IdeSession *self,
+ GAsyncResult *result,
+ GError **error);
+void ide_session_save_async (IdeSession *self,
+ IdeWorkbench *workbench,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean ide_session_save_finish (IdeSession *self,
+ GAsyncResult *result,
+ GError **error);
+
+G_END_DECLS
diff --git a/src/libide/session/ide-session.c b/src/libide/gui/ide-session.c
similarity index 84%
rename from src/libide/session/ide-session.c
rename to src/libide/gui/ide-session.c
index df18ff00f..29d5fe81d 100644
--- a/src/libide/session/ide-session.c
+++ b/src/libide/gui/ide-session.c
@@ -23,19 +23,16 @@
#include "config.h"
#include <libpeas/peas.h>
+#include <libide-plugins.h>
+#include <libide-threading.h>
-#include "ide-context.h"
-#include "ide-debug.h"
-#include "ide-global.h"
-#include "ide-session.h"
-
-#include "session/ide-session-addin.h"
-#include "threading/ide-task.h"
+#include "ide-session-addin.h"
+#include "ide-session-private.h"
struct _IdeSession
{
- IdeObject parent_instance;
- PeasExtensionSet *addins;
+ IdeObject parent_instance;
+ IdeExtensionSetAdapter *addins;
};
typedef struct
@@ -48,9 +45,10 @@ typedef struct
typedef struct
{
- GPtrArray *addins;
- GVariant *state;
- gint active;
+ IdeWorkbench *workbench;
+ GPtrArray *addins;
+ GVariant *state;
+ gint active;
} Restore;
G_DEFINE_TYPE (IdeSession, ide_session, IDE_TYPE_OBJECT)
@@ -62,6 +60,7 @@ restore_free (Restore *r)
g_clear_pointer (&r->addins, g_ptr_array_unref);
g_clear_pointer (&r->state, g_variant_unref);
+ g_clear_object (&r->workbench);
g_slice_free (Restore, r);
}
@@ -79,14 +78,15 @@ save_free (Save *s)
}
static void
-collect_addins_cb (PeasExtensionSet *set,
- PeasPluginInfo *plugin_info,
- PeasExtension *exten,
- gpointer user_data)
+collect_addins_cb (IdeExtensionSetAdapter *set,
+ PeasPluginInfo *plugin_info,
+ PeasExtension *exten,
+ gpointer user_data)
{
GPtrArray *ar = user_data;
- g_assert (PEAS_IS_EXTENSION_SET (set));
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_EXTENSION_SET_ADAPTER (set));
g_assert (plugin_info != NULL);
g_assert (IDE_IS_SESSION_ADDIN (exten));
g_assert (ar != NULL);
@@ -95,42 +95,48 @@ collect_addins_cb (PeasExtensionSet *set,
}
static void
-ide_session_dispose (GObject *object)
+ide_session_destroy (IdeObject *object)
{
IdeSession *self = (IdeSession *)object;
IDE_ENTRY;
- g_clear_object (&self->addins);
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_SESSION (self));
+
+ ide_clear_and_destroy_object (&self->addins);
- G_OBJECT_CLASS (ide_session_parent_class)->dispose (object);
+ IDE_OBJECT_CLASS (ide_session_parent_class)->destroy (object);
IDE_EXIT;
}
static void
-ide_session_constructed (GObject *object)
+ide_session_parent_set (IdeObject *object,
+ IdeObject *parent)
{
IdeSession *self = (IdeSession *)object;
- IdeContext *context;
- G_OBJECT_CLASS (ide_session_parent_class)->constructed (object);
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_SESSION (self));
+ g_assert (!parent || IDE_IS_OBJECT (parent));
- context = ide_object_get_context (IDE_OBJECT (self));
+ if (parent == NULL)
+ return;
- self->addins = peas_extension_set_new (peas_engine_get_default (),
- IDE_TYPE_SESSION_ADDIN,
- "context", context,
- NULL);
+ self->addins = ide_extension_set_adapter_new (IDE_OBJECT (self),
+ peas_engine_get_default (),
+ IDE_TYPE_SESSION_ADDIN,
+ NULL, NULL);
}
static void
ide_session_class_init (IdeSessionClass *klass)
{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ IdeObjectClass *i_object_class = IDE_OBJECT_CLASS (klass);
- object_class->constructed = ide_session_constructed;
- object_class->dispose = ide_session_dispose;
+ i_object_class->destroy = ide_session_destroy;
+ i_object_class->parent_set = ide_session_parent_set;
}
static void
@@ -240,6 +246,7 @@ ide_session_restore_load_cb (GObject *object,
NULL);
ide_session_addin_restore_async (addin,
+ r->workbench,
state,
cancellable,
ide_session_restore_addin_restore_cb,
@@ -252,6 +259,7 @@ ide_session_restore_load_cb (GObject *object,
/**
* ide_session_restore_async:
* @self: an #IdeSession
+ * @workbench: an #IdeWorkbench
* @cancellable: (nullable): a #GCancellbale or %NULL
* @callback: the callback to execute upon completion
* @user_data: user data for callback
@@ -260,10 +268,11 @@ ide_session_restore_load_cb (GObject *object,
* the point it was last saved (typically upon shutdown). This includes
* open documents and editor splits to the degree possible.
*
- * Since: 3.32
+ * Since: 3.30
*/
void
ide_session_restore_async (IdeSession *self,
+ IdeWorkbench *workbench,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
@@ -276,14 +285,16 @@ ide_session_restore_async (IdeSession *self,
IDE_ENTRY;
g_return_if_fail (IDE_IS_SESSION (self));
+ g_return_if_fail (IDE_IS_WORKBENCH (workbench));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
task = ide_task_new (self, cancellable, callback, user_data);
ide_task_set_source_tag (task, ide_session_restore_async);
r = g_slice_new0 (Restore);
+ r->workbench = g_object_ref (workbench);
r->addins = g_ptr_array_new_with_free_func (g_object_unref);
- peas_extension_set_foreach (self->addins, collect_addins_cb, r->addins);
+ ide_extension_set_adapter_foreach (self->addins, collect_addins_cb, r->addins);
r->active = r->addins->len;
ide_task_set_task_data (task, r, restore_free);
@@ -421,6 +432,7 @@ ide_session_save_addin_save_cb (GObject *object,
/**
* ide_session_save_async:
* @self: an #IdeSession
+ * @workbench: an #IdeWorkbench
* @cancellable: (nullable): a #GCancellable, or %NULL
* @callback: a callback to execute upon completion
* @user_data: user data for @callback
@@ -429,10 +441,11 @@ ide_session_save_addin_save_cb (GObject *object,
* so that the project may be restored to the current layout when the project
* is re-opened at a later time.
*
- * Since: 3.32
+ * Since: 3.30
*/
void
ide_session_save_async (IdeSession *self,
+ IdeWorkbench *workbench,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
@@ -443,6 +456,7 @@ ide_session_save_async (IdeSession *self,
IDE_ENTRY;
g_return_if_fail (IDE_IS_SESSION (self));
+ g_return_if_fail (IDE_IS_WORKBENCH (workbench));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
task = ide_task_new (self, cancellable, callback, user_data);
@@ -450,7 +464,7 @@ ide_session_save_async (IdeSession *self,
s = g_slice_new0 (Save);
s->addins = g_ptr_array_new_with_free_func (g_object_unref);
- peas_extension_set_foreach (self->addins, collect_addins_cb, s->addins);
+ ide_extension_set_adapter_foreach (self->addins, collect_addins_cb, s->addins);
s->active = s->addins->len;
g_variant_dict_init (&s->dict, NULL);
s->dict_needs_clear = TRUE;
@@ -467,6 +481,7 @@ ide_session_save_async (IdeSession *self,
IdeSessionAddin *addin = g_ptr_array_index (s->addins, i);
ide_session_addin_save_async (addin,
+ workbench,
cancellable,
ide_session_save_addin_save_cb,
g_object_ref (task));
@@ -495,3 +510,9 @@ ide_session_save_finish (IdeSession *self,
IDE_RETURN (ret);
}
+
+IdeSession *
+ide_session_new (void)
+{
+ return g_object_new (IDE_TYPE_SESSION, NULL);
+}
diff --git a/src/libide/layout/ide-shortcut-label.h b/src/libide/gui/ide-shortcut-label-private.h
similarity index 98%
rename from src/libide/layout/ide-shortcut-label.h
rename to src/libide/gui/ide-shortcut-label-private.h
index 8789afd0d..5d27f230f 100644
--- a/src/libide/layout/ide-shortcut-label.h
+++ b/src/libide/gui/ide-shortcut-label-private.h
@@ -1,4 +1,4 @@
-/* ide-shortcut-label.h
+/* ide-shortcut-label-private.h
*
* Copyright 2017-2019 Christian Hergert <chergert redhat com>
*
diff --git a/src/libide/layout/ide-shortcut-label.c b/src/libide/gui/ide-shortcut-label.c
similarity index 99%
rename from src/libide/layout/ide-shortcut-label.c
rename to src/libide/gui/ide-shortcut-label.c
index 38d2b1ca5..d561f5d9c 100644
--- a/src/libide/layout/ide-shortcut-label.c
+++ b/src/libide/gui/ide-shortcut-label.c
@@ -24,7 +24,7 @@
#include <dazzle.h>
-#include "layout/ide-shortcut-label.h"
+#include "ide-shortcut-label-private.h"
struct _IdeShortcutLabel
{
diff --git a/src/libide/gui/ide-shortcuts-window-private.h b/src/libide/gui/ide-shortcuts-window-private.h
new file mode 100644
index 000000000..5ce88ce59
--- /dev/null
+++ b/src/libide/gui/ide-shortcuts-window-private.h
@@ -0,0 +1,31 @@
+/* ide-shortcuts-window.h
+ *
+ * Copyright 2015-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_SHORTCUTS_WINDOW (ide_shortcuts_window_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeShortcutsWindow, ide_shortcuts_window, IDE, SHORTCUTS_WINDOW, GtkShortcutsWindow)
+
+G_END_DECLS
diff --git a/src/libide/gui/ide-shortcuts-window.c b/src/libide/gui/ide-shortcuts-window.c
new file mode 100644
index 000000000..5f7dc3494
--- /dev/null
+++ b/src/libide/gui/ide-shortcuts-window.c
@@ -0,0 +1,48 @@
+/* ide-shortcuts-window.c
+ *
+ * Copyright 2015-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-shortcuts-window"
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include "ide-shortcuts-window-private.h"
+
+struct _IdeShortcutsWindow
+{
+ GtkShortcutsWindow parent_instance;
+};
+
+G_DEFINE_TYPE (IdeShortcutsWindow, ide_shortcuts_window, GTK_TYPE_SHORTCUTS_WINDOW)
+
+static void
+ide_shortcuts_window_class_init (IdeShortcutsWindowClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/libide-gui/ui/ide-shortcuts-window.ui");
+}
+
+static void
+ide_shortcuts_window_init (IdeShortcutsWindow *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+}
diff --git a/src/libide/gui/ide-shortcuts-window.ui b/src/libide/gui/ide-shortcuts-window.ui
new file mode 100644
index 000000000..8c3191238
--- /dev/null
+++ b/src/libide/gui/ide-shortcuts-window.ui
@@ -0,0 +1,547 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.19 -->
+ <template class="IdeShortcutsWindow">
+ <property name="modal">true</property>
+ <child>
+ <object class="GtkShortcutsSection">
+ <property name="visible">true</property>
+ <property name="section-name">editor</property>
+ <property name="title" translatable="yes" context="shortcut window">Editor Shortcuts</property>
+ <child>
+ <object class="GtkShortcutsGroup">
+ <property name="visible">true</property>
+ <property name="title" translatable="yes" context="shortcut window">General</property>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="title" translatable="yes" context="shortcut window">Global Search</property>
+ <property name="accelerator"><ctrl>period</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="title" translatable="yes" context="shortcut window">Preferences</property>
+ <property name="accelerator"><ctrl>comma</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="title" translatable="yes" context="shortcut window">Command Bar</property>
+ <property name="accelerator"><ctrl>Return</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="title" translatable="yes" context="shortcut window">Terminal</property>
+ <property name="accelerator"><ctrl><shift>t</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="title" translatable="yes" context="shortcut window">Terminal in Build
Runtime</property>
+ <property name="accelerator"><ctrl><alt><shift>t</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="title" translatable="yes" context="shortcut window">Keyboard
Shortcuts</property>
+ <property name="accelerator"><ctrl><shift>question</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="title" translatable="yes" context="shortcut window">Toggle Focus
Mode</property>
+ <property name="accelerator">F11</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsGroup">
+ <property name="visible">true</property>
+ <property name="title" translatable="yes" context="shortcut window">Panels</property>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="title" translatable="yes" context="shortcut window">Toggle left
panel</property>
+ <property name="accelerator">F9</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="title" translatable="yes" context="shortcut window">Toggle bottom
panel</property>
+ <property name="accelerator"><ctrl>F9</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsGroup">
+ <property name="visible">true</property>
+ <property name="title" translatable="yes" context="shortcut window">Files</property>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl>n</property>
+ <property name="title" translatable="yes" context="shortcut window">Create new
document</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl>o</property>
+ <property name="title" translatable="yes" context="shortcut window">Open a
document</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl>s</property>
+ <property name="title" translatable="yes" context="shortcut window">Save the
document</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl><alt>s</property>
+ <property name="title" translatable="yes" context="shortcut window">Save all
documents</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl>w</property>
+ <property name="title" translatable="yes" context="shortcut window">Close the
document</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><primary><shift>w</property>
+ <property name="title" translatable="yes" context="shortcut window">Close all
documents</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl><alt>Page_Down</property>
+ <property name="title" translatable="yes" context="shortcut window">Switch to the next
document</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl><alt>Page_Up</property>
+ <property name="title" translatable="yes" context="shortcut window">Switch to the previous
document</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl>k</property>
+ <property name="title" translatable="yes" context="shortcut window">Show list of open
documents</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsGroup">
+ <property name="visible">true</property>
+ <property name="title" translatable="yes" context="shortcut window">Find and replace</property>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl>f</property>
+ <property name="title" translatable="yes" context="shortcut window">Find</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl>h</property>
+ <property name="title" translatable="yes" context="shortcut window">Find and
replace</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl>g</property>
+ <property name="title" translatable="yes" context="shortcut window">Find the next
match</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl><shift>g</property>
+ <property name="title" translatable="yes" context="shortcut window">Find the previous
match</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl><shift>k</property>
+ <property name="title" translatable="yes" context="shortcut window">Clear
highlight</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsGroup">
+ <property name="visible">true</property>
+ <property name="title" translatable="yes" context="shortcut window">Copy and Paste</property>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl>c</property>
+ <property name="title" translatable="yes" context="shortcut window">Copy selected text to
clipboard</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl>x</property>
+ <property name="title" translatable="yes" context="shortcut window">Cut selected text to
clipboard</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl>v</property>
+ <property name="title" translatable="yes" context="shortcut window">Paste text from
clipboard</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl><alt>d</property>
+ <property name="title" translatable="yes" context="shortcut window">Duplicate current line
or selection</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsGroup">
+ <property name="visible">true</property>
+ <property name="title" translatable="yes" context="shortcut window">Undo and Redo</property>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl>z</property>
+ <property name="title" translatable="yes" context="shortcut window">Undo previous
command</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl><shift>z</property>
+ <property name="title" translatable="yes" context="shortcut window">Redo previous
command</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsGroup">
+ <property name="visible">true</property>
+ <property name="title" translatable="yes" context="shortcut window">Editing</property>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl><shift>a</property>
+ <property name="title" translatable="yes" context="shortcut window">Increment number at
cursor</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl><shift>x</property>
+ <property name="title" translatable="yes" context="shortcut window">Decrement number at
cursor</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl>j</property>
+ <property name="title" translatable="yes" context="shortcut window">Join selected
lines</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl>space</property>
+ <property name="title" translatable="yes" context="shortcut window">Show completion
window</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator">Insert</property>
+ <property name="title" translatable="yes" context="shortcut window">Toggle
overwrite</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl><alt>i</property>
+ <property name="title" translatable="yes" context="shortcut window">Reindent line</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl>d</property>
+ <property name="title" translatable="yes" context="shortcut window">Delete line</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl><shift>r</property>
+ <property name="title" translatable="yes" context="shortcut window">Rename symbol</property>
+ <property name="subtitle" translatable="yes" context="shortcut window">Requires semantic
language support</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsGroup">
+ <property name="visible">true</property>
+ <property name="title" translatable="yes" context="shortcut window">Navigation</property>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><alt>n</property>
+ <property name="title" translatable="yes" context="shortcut window">Move to next error in
file</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><alt>p</property>
+ <property name="title" translatable="yes" context="shortcut window">Move to previous error
in file</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><shift><alt>Left</property>
+ <property name="title" translatable="yes" context="shortcut window">Move to previous edit
location</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><shift><alt>Right</property>
+ <property name="title" translatable="yes" context="shortcut window">Move to next edit
location</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><alt>period</property>
+ <property name="title" translatable="yes" context="shortcut window">Jump to definition of
symbol</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><alt><shift>Up</property>
+ <property name="title" translatable="yes" context="shortcut window">Move viewport up within
the file</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><alt><shift>Down</property>
+ <property name="title" translatable="yes" context="shortcut window">Move viewport down
within the file</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><alt><shift>End</property>
+ <property name="title" translatable="yes" context="shortcut window">Move viewport to end of
file</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><alt><shift>Home</property>
+ <property name="title" translatable="yes" context="shortcut window">Move viewport to
beginning of file</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl>percent</property>
+ <property name="title" translatable="yes" context="shortcut window">Move to matching
bracket</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsGroup">
+ <property name="visible">true</property>
+ <property name="title" translatable="yes" context="shortcut window">Selections</property>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl>a</property>
+ <property name="title" translatable="yes" context="shortcut window">Select all</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl>backslash</property>
+ <property name="title" translatable="yes" context="shortcut window">Unselect all</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsGroup">
+ <property name="visible">true</property>
+ <property name="title" translatable="yes" context="shortcut window">Build and Run</property>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl>F7</property>
+ <property name="title" translatable="yes" context="shortcut window">Build</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl>F5</property>
+ <property name="title" translatable="yes" context="shortcut window">Run</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl>F8</property>
+ <property name="title" translatable="yes" context="shortcut window">Profile</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsGroup">
+ <property name="visible">true</property>
+ <property name="title" translatable="yes" context="shortcut window">Touchpad gestures</property>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="shortcut-type">gesture-two-finger-swipe-right</property>
+ <property name="title" translatable="yes" context="shortcut window">Switch to the next
document</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="shortcut-type">gesture-two-finger-swipe-left</property>
+ <property name="title" translatable="yes" context="shortcut window">Switch to the previous
document</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsSection">
+ <property name="visible">true</property>
+ <property name="section-name">terminal</property>
+ <property name="title" translatable="yes" context="shortcut window">Terminal Shortcuts</property>
+ <child>
+ <object class="GtkShortcutsGroup">
+ <property name="visible">true</property>
+ <property name="title" translatable="yes" context="shortcut window">General</property>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="title" translatable="yes" context="shortcut window">Global Search</property>
+ <property name="accelerator"><ctrl>period</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="title" translatable="yes" context="shortcut window">Preferences</property>
+ <property name="accelerator"><ctrl>comma</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="title" translatable="yes" context="shortcut window">Command Bar</property>
+ <property name="accelerator"><ctrl>Return</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="title" translatable="yes" context="shortcut window">Terminal</property>
+ <property name="accelerator"><ctrl><shift>t</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="title" translatable="yes" context="shortcut window">Terminal in Build
Runtime</property>
+ <property name="accelerator"><ctrl><alt><shift>t</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="title" translatable="yes" context="shortcut window">Keyboard
Shortcuts</property>
+ <property name="accelerator"><ctrl><shift>question</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsGroup">
+ <property name="visible">true</property>
+ <property name="title" translatable="yes" context="shortcut window">Copy and Paste</property>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl><shift>c</property>
+ <property name="title" translatable="yes" context="shortcut window">Copy selected text to
clipboard</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl><shift>v</property>
+ <property name="title" translatable="yes" context="shortcut window">Paste text from
clipboard</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkShortcutsGroup">
+ <property name="visible">true</property>
+ <property name="title" translatable="yes" context="shortcut window">Search</property>
+ <child>
+ <object class="GtkShortcutsShortcut">
+ <property name="visible">true</property>
+ <property name="accelerator"><ctrl><shift>f</property>
+ <property name="title" translatable="yes" context="shortcut window">Find text within
terminal</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/src/libide/gui/ide-surface.c b/src/libide/gui/ide-surface.c
new file mode 100644
index 000000000..43224679c
--- /dev/null
+++ b/src/libide/gui/ide-surface.c
@@ -0,0 +1,259 @@
+/* ide-surface.c
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-surface"
+
+#include "config.h"
+
+#include "ide-gui-private.h"
+#include "ide-surface.h"
+
+typedef struct
+{
+ gchar *icon_name;
+ gchar *title;
+} IdeSurfacePrivate;
+
+enum {
+ PROP_0,
+ PROP_ICON_NAME,
+ PROP_TITLE,
+ N_PROPS
+};
+
+static void dock_item_iface_init (DzlDockItemInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (IdeSurface, ide_surface, DZL_TYPE_DOCK_BIN,
+ G_ADD_PRIVATE (IdeSurface)
+ G_IMPLEMENT_INTERFACE (DZL_TYPE_DOCK_ITEM, dock_item_iface_init))
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+ide_surface_finalize (GObject *object)
+{
+ IdeSurface *self = (IdeSurface *)object;
+ IdeSurfacePrivate *priv = ide_surface_get_instance_private (self);
+
+ g_clear_pointer (&priv->icon_name, g_free);
+ g_clear_pointer (&priv->title, g_free);
+
+ G_OBJECT_CLASS (ide_surface_parent_class)->finalize (object);
+}
+
+static void
+ide_surface_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdeSurface *self = IDE_SURFACE (object);
+
+ switch (prop_id)
+ {
+ case PROP_ICON_NAME:
+ g_value_set_string (value, dzl_dock_item_get_icon_name (DZL_DOCK_ITEM (self)));
+ break;
+
+ case PROP_TITLE:
+ g_value_set_string (value, dzl_dock_item_get_title (DZL_DOCK_ITEM (self)));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_surface_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdeSurface *self = IDE_SURFACE (object);
+
+ switch (prop_id)
+ {
+ case PROP_ICON_NAME:
+ ide_surface_set_icon_name (self, g_value_get_string (value));
+ break;
+
+ case PROP_TITLE:
+ ide_surface_set_title (self, g_value_get_string (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_surface_class_init (IdeSurfaceClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = ide_surface_finalize;
+ object_class->get_property = ide_surface_get_property;
+ object_class->set_property = ide_surface_set_property;
+
+ properties [PROP_ICON_NAME] =
+ g_param_spec_string ("icon-name",
+ "Icon Name",
+ "The icon name for the surface",
+ NULL,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_TITLE] =
+ g_param_spec_string ("title",
+ "Title",
+ "The title for the surface, if any",
+ NULL,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+ide_surface_init (IdeSurface *self)
+{
+}
+
+/**
+ * ide_surface_new:
+ *
+ * Creates a new #IdeSurface.
+ *
+ * Surfaces contain the main window contents that are placed inside of an
+ * #IdeWorkspace (window). You may have multiple surfaces in a workspace,
+ * and the user can switch between them.
+ *
+ * Returns: (transfer full): an #IdeSurface or %NULL
+ *
+ * Since: 3.32
+ */
+GtkWidget *
+ide_surface_new (void)
+{
+ return g_object_new (IDE_TYPE_SURFACE, NULL);
+}
+
+void
+ide_surface_set_icon_name (IdeSurface *self,
+ const gchar *icon_name)
+{
+ IdeSurfacePrivate *priv = ide_surface_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_SURFACE (self));
+
+ if (!ide_str_equal0 (priv->icon_name, icon_name))
+ {
+ g_free (priv->icon_name);
+ priv->icon_name = g_strdup (icon_name);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ICON_NAME]);
+ }
+}
+
+void
+ide_surface_set_title (IdeSurface *self,
+ const gchar *title)
+{
+ IdeSurfacePrivate *priv = ide_surface_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_SURFACE (self));
+
+ if (!ide_str_equal0 (priv->title, title))
+ {
+ g_free (priv->title);
+ priv->title = g_strdup (title);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TITLE]);
+ }
+}
+
+/**
+ * ide_surface_foreach_page:
+ * @self: a #IdeSurface
+ * @callback: (scope call): callback to execute for each page
+ * @user_data: closure data for @callback
+ *
+ * Calls @callback for every page found within the surface @self.
+ *
+ * Since: 3.32
+ */
+void
+ide_surface_foreach_page (IdeSurface *self,
+ GtkCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (IDE_IS_SURFACE (self));
+ g_return_if_fail (callback != NULL);
+
+ if (IDE_SURFACE_GET_CLASS (self)->foreach_page)
+ IDE_SURFACE_GET_CLASS (self)->foreach_page (self, callback, user_data);
+}
+
+static gchar *
+ide_surface_real_get_icon_name (DzlDockItem *item)
+{
+ IdeSurface *self = (IdeSurface *)item;
+ IdeSurfacePrivate *priv = ide_surface_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_SURFACE (self), NULL);
+
+ return g_strdup (priv->icon_name);
+}
+
+static gchar *
+ide_surface_real_get_title (DzlDockItem *item)
+{
+ IdeSurface *self = (IdeSurface *)item;
+ IdeSurfacePrivate *priv = ide_surface_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_SURFACE (self), NULL);
+
+ return g_strdup (priv->title);
+}
+
+static void
+dock_item_iface_init (DzlDockItemInterface *iface)
+{
+ iface->get_icon_name = ide_surface_real_get_icon_name;
+ iface->get_title = ide_surface_real_get_title;
+}
+
+gboolean
+ide_surface_agree_to_shutdown (IdeSurface *self)
+{
+ g_return_val_if_fail (IDE_IS_SURFACE (self), FALSE);
+
+ if (IDE_SURFACE_GET_CLASS (self)->agree_to_shutdown)
+ return IDE_SURFACE_GET_CLASS (self)->agree_to_shutdown (self);
+
+ return TRUE;
+}
+
+void
+_ide_surface_set_fullscreen (IdeSurface *self,
+ gboolean fullscreen)
+{
+ g_return_if_fail (IDE_IS_SURFACE (self));
+
+ if (IDE_SURFACE_GET_CLASS (self)->set_fullscreen)
+ IDE_SURFACE_GET_CLASS (self)->set_fullscreen (self, fullscreen);
+}
diff --git a/src/libide/gui/ide-surface.h b/src/libide/gui/ide-surface.h
new file mode 100644
index 000000000..2be97c69f
--- /dev/null
+++ b/src/libide/gui/ide-surface.h
@@ -0,0 +1,67 @@
+/* ide-surface.h
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#if !defined (IDE_GUI_INSIDE) && !defined (IDE_GUI_COMPILATION)
+# error "Only <libide-gui.h> can be included directly."
+#endif
+
+#include <dazzle.h>
+#include <libide-core.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_SURFACE (ide_surface_get_type())
+
+IDE_AVAILABLE_IN_3_32
+G_DECLARE_DERIVABLE_TYPE (IdeSurface, ide_surface, IDE, SURFACE, DzlDockBin)
+
+struct _IdeSurfaceClass
+{
+ DzlDockBinClass parent_class;
+
+ void (*foreach_page) (IdeSurface *self,
+ GtkCallback callback,
+ gpointer user_data);
+ gboolean (*agree_to_shutdown) (IdeSurface *self);
+ void (*set_fullscreen) (IdeSurface *self,
+ gboolean fullscreen);
+
+ /*< private >*/
+ gpointer _reserved[16];
+};
+
+IDE_AVAILABLE_IN_3_32
+GtkWidget *ide_surface_new (void);
+IDE_AVAILABLE_IN_3_32
+void ide_surface_set_icon_name (IdeSurface *self,
+ const gchar *icon_name);
+IDE_AVAILABLE_IN_3_32
+void ide_surface_set_title (IdeSurface *self,
+ const gchar *title);
+IDE_AVAILABLE_IN_3_32
+void ide_surface_foreach_page (IdeSurface *self,
+ GtkCallback callback,
+ gpointer user_data);
+IDE_AVAILABLE_IN_3_32
+gboolean ide_surface_agree_to_shutdown (IdeSurface *self);
+
+G_END_DECLS
diff --git a/src/libide/gui/ide-surfaces-button.c b/src/libide/gui/ide-surfaces-button.c
new file mode 100644
index 000000000..447ebf475
--- /dev/null
+++ b/src/libide/gui/ide-surfaces-button.c
@@ -0,0 +1,107 @@
+/* ide-surfaces-button.c
+ *
+ * Copyright 2018 Christian Hergert <unknown domain org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-surfaces-button"
+
+#include "config.h"
+
+#include <libide-core.h>
+
+#include "ide-surfaces-button.h"
+
+struct _IdeSurfacesButton
+{
+ DzlMenuButton parent_instance;
+};
+
+G_DEFINE_TYPE (IdeSurfacesButton, ide_surfaces_button, DZL_TYPE_MENU_BUTTON)
+
+static void
+ide_surfaces_button_items_changed_cb (IdeSurfacesButton *self,
+ guint position,
+ guint added,
+ guint removed,
+ GMenuModel *model)
+{
+ gboolean visible = FALSE;
+ guint n_items;
+ guint count = 0;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_SURFACES_BUTTON (self));
+ g_assert (G_IS_MENU_MODEL (model));
+
+ /* We either have multiple sections, or a single section with
+ * possibly multiple children. Any of these means visible.
+ */
+
+ n_items = g_menu_model_get_n_items (model);
+ visible = n_items > 1;
+
+ for (guint i = 0; !visible && i < n_items; i++)
+ {
+ g_autoptr(GMenuLinkIter) iter = g_menu_model_iterate_item_links (model, i);
+
+ while (g_menu_link_iter_next (iter))
+ {
+ g_autoptr(GMenuModel) child = g_menu_link_iter_get_value (iter);
+ count += g_menu_model_get_n_items (child);
+ }
+
+ visible = count > 1;
+ }
+
+ gtk_widget_set_visible (GTK_WIDGET (self), visible);
+}
+
+static void
+ide_surfaces_button_notify_model (IdeSurfacesButton *self,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ GMenuModel *model;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_SURFACES_BUTTON (self));
+
+ if ((model = dzl_menu_button_get_model (DZL_MENU_BUTTON (self))))
+ {
+ g_signal_connect_object (model,
+ "items-changed",
+ G_CALLBACK (ide_surfaces_button_items_changed_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ ide_surfaces_button_items_changed_cb (self, 0, 0, 0, model);
+ }
+}
+
+static void
+ide_surfaces_button_class_init (IdeSurfacesButtonClass *klass)
+{
+}
+
+static void
+ide_surfaces_button_init (IdeSurfacesButton *self)
+{
+ g_signal_connect (self,
+ "notify::model",
+ G_CALLBACK (ide_surfaces_button_notify_model),
+ NULL);
+}
diff --git a/src/libide/layout/ide-layout.h b/src/libide/gui/ide-surfaces-button.h
similarity index 66%
rename from src/libide/layout/ide-layout.h
rename to src/libide/gui/ide-surfaces-button.h
index b5d27a51c..d9efe9808 100644
--- a/src/libide/layout/ide-layout.h
+++ b/src/libide/gui/ide-surfaces-button.h
@@ -1,6 +1,6 @@
-/* ide-layout.h
+/* ide-surfaces-button.h
*
- * Copyright 2015-2019 Christian Hergert <christian hergert me>
+ * Copyright 2018 Christian Hergert <unknown domain org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,23 +20,18 @@
#pragma once
-#include <dazzle.h>
+#if !defined (IDE_GUI_INSIDE) && !defined (IDE_GUI_COMPILATION)
+# error "Only <libide-gui.h> can be included directly."
+#endif
-#include "ide-version-macros.h"
+#include <libide-core.h>
+#include <dazzle.h>
G_BEGIN_DECLS
-#define IDE_TYPE_LAYOUT (ide_layout_get_type())
+#define IDE_TYPE_SURFACES_BUTTON (ide_surfaces_button_get_type())
IDE_AVAILABLE_IN_3_32
-G_DECLARE_DERIVABLE_TYPE (IdeLayout, ide_layout, IDE, LAYOUT, DzlDockBin)
-
-struct _IdeLayoutClass
-{
- DzlDockBinClass parent_class;
-
- /*< private >*/
- gpointer _reserved[8];
-};
+G_DECLARE_FINAL_TYPE (IdeSurfacesButton, ide_surfaces_button, IDE, SURFACES_BUTTON, DzlMenuButton)
G_END_DECLS
diff --git a/src/libide/search/ide-tagged-entry.c b/src/libide/gui/ide-tagged-entry.c
similarity index 99%
rename from src/libide/search/ide-tagged-entry.c
rename to src/libide/gui/ide-tagged-entry.c
index fac6a3b01..e719bb0bf 100644
--- a/src/libide/search/ide-tagged-entry.c
+++ b/src/libide/gui/ide-tagged-entry.c
@@ -18,6 +18,8 @@
*
* Author: Cosimo Cecchi <cosimoc redhat com>
*
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "config.h"
diff --git a/src/libide/search/ide-tagged-entry.h b/src/libide/gui/ide-tagged-entry.h
similarity index 97%
rename from src/libide/search/ide-tagged-entry.h
rename to src/libide/gui/ide-tagged-entry.h
index a49e6eebc..261985d2d 100644
--- a/src/libide/search/ide-tagged-entry.h
+++ b/src/libide/gui/ide-tagged-entry.h
@@ -3,29 +3,30 @@
* Copyright 2013 Ignacio Casal Quinteiro
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
+ * it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 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 Lesser General Public
+ * 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 Lesser General Public License
+ * You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Author: Cosimo Cecchi <cosimoc redhat com>
*
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
*/
#ifndef __IDE_TAGGED_ENTRY_H__
#define __IDE_TAGGED_ENTRY_H__
#include <gtk/gtk.h>
-
-#include "ide-version-macros.h"
+#include <libide-core.h>
G_BEGIN_DECLS
diff --git a/src/libide/gui/ide-transient-sidebar.c b/src/libide/gui/ide-transient-sidebar.c
new file mode 100644
index 000000000..2fa15b032
--- /dev/null
+++ b/src/libide/gui/ide-transient-sidebar.c
@@ -0,0 +1,355 @@
+/* ide-transient-sidebar.c
+ *
+ * Copyright 2017-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-transient-sidebar"
+
+#include "config.h"
+
+#include "ide-frame.h"
+#include "ide-grid.h"
+#include "ide-transient-sidebar.h"
+
+typedef struct
+{
+ DzlSignalGroup *toplevel_signals;
+ GWeakRef page_ref;
+ gint hold_count;
+} IdeTransientSidebarPrivate;
+
+static void ide_transient_sidebar_page_destroyed (IdeTransientSidebar *self,
+ IdePage *page);
+
+G_DEFINE_TYPE_WITH_PRIVATE (IdeTransientSidebar, ide_transient_sidebar, IDE_TYPE_PANEL)
+
+static gboolean
+has_page_related_focus (IdeTransientSidebar *self)
+{
+ IdeTransientSidebarPrivate *priv = ide_transient_sidebar_get_instance_private (self);
+ g_autoptr(IdePage) page = NULL;
+ GtkWidget *focus_page;
+ GtkWidget *toplevel;
+ GtkWidget *focus;
+ GtkWidget *grid;
+
+ g_assert (IDE_IS_TRANSIENT_SIDEBAR (self));
+
+ /* If there is no page, then nothing more to do */
+ page = g_weak_ref_get (&priv->page_ref);
+ if (page == NULL)
+ return FALSE;
+
+ /* We need the toplevel to get the current focus */
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self));
+ if (!GTK_IS_WINDOW (toplevel))
+ return FALSE;
+
+ /* Synthesize succes when there is no focus, this can happen inbetween
+ * various state transitions.
+ */
+ focus = gtk_window_get_focus (GTK_WINDOW (toplevel));
+ if (focus == NULL)
+ return TRUE;
+
+ /* If focus is inside this widget, then we don't want to hide */
+ if (gtk_widget_is_ancestor (focus, GTK_WIDGET (self)))
+ return TRUE;
+
+ /* If focus is in the page, then we definitely don't want to hide */
+ if (gtk_widget_is_ancestor (focus, GTK_WIDGET (page)))
+ return TRUE;
+
+ /* If the focus has entered another page, then we can release. */
+ focus_page = gtk_widget_get_ancestor (focus, IDE_TYPE_PAGE);
+ if (focus_page && focus_page != GTK_WIDGET (page))
+ return FALSE;
+
+ /* If we found ourselves a grid, and it has no pages in it, we shall
+ * expect that there are no more pages to apply.
+ */
+ grid = gtk_widget_get_ancestor (focus, IDE_TYPE_GRID);
+ if (grid != NULL &&
+ ide_grid_count_pages (IDE_GRID (grid)) == 0)
+ return FALSE;
+
+ /* Focus hasn't landed anywhere that indicates to us that the
+ * page definitely isn't visible anymore, so we can just keep
+ * the panel visible for now.
+ */
+
+ return TRUE;
+}
+
+static void
+set_visible (IdeTransientSidebar *self,
+ gboolean visible)
+{
+ const gchar *prop_name;
+ GtkPositionType pos;
+ GtkWidget *bin;
+
+ g_assert (IDE_IS_TRANSIENT_SIDEBAR (self));
+
+ if (!(bin = gtk_widget_get_ancestor (GTK_WIDGET (self), DZL_TYPE_DOCK_BIN)))
+ {
+ g_warning ("Failed to locate DzlDockBin for transition");
+ return;
+ }
+
+ gtk_container_child_get (GTK_CONTAINER (bin), GTK_WIDGET (self),
+ "position", &pos,
+ NULL);
+
+ switch (pos)
+ {
+ case GTK_POS_TOP:
+ prop_name = "top-visible";
+ break;
+
+ case GTK_POS_BOTTOM:
+ prop_name = "bottom-visible";
+ break;
+
+ case GTK_POS_LEFT:
+ prop_name = "left-visible";
+ break;
+
+ case GTK_POS_RIGHT:
+ prop_name = "right-visible";
+ break;
+
+ default:
+ g_return_if_reached ();
+ }
+
+ g_object_set (bin, prop_name, visible, NULL);
+}
+
+static void
+ide_transient_sidebar_after_set_focus (IdeTransientSidebar *self,
+ GtkWidget *focus,
+ GtkWindow *toplevel)
+{
+ IdeTransientSidebarPrivate *priv = ide_transient_sidebar_get_instance_private (self);
+
+ g_assert (IDE_IS_TRANSIENT_SIDEBAR (self));
+ g_assert (!toplevel || GTK_IS_WINDOW (toplevel));
+ g_assert (priv->hold_count >= 0);
+
+ if (priv->hold_count > 0)
+ return;
+
+ /*
+ * If we are currently visible, then check to see if the focus has gone
+ * somewhere outside the panel or the page. If so, we need to dismiss
+ * the panel.
+ *
+ * We try to be tolerant of sibling focus on such things like the stack
+ * header.
+ */
+ if (gtk_widget_get_visible (GTK_WIDGET (self)))
+ {
+ if (!has_page_related_focus (self))
+ {
+ g_autoptr(GtkWidget) old_page = g_weak_ref_get (&priv->page_ref);
+
+ if (old_page != NULL)
+ g_signal_handlers_disconnect_by_func (old_page,
+ G_CALLBACK (ide_transient_sidebar_page_destroyed),
+ self);
+
+ set_visible (self, FALSE);
+ g_weak_ref_set (&priv->page_ref, NULL);
+ }
+ }
+}
+
+static void
+ide_transient_sidebar_page_destroyed (IdeTransientSidebar *self,
+ IdePage *page)
+{
+ IdeTransientSidebarPrivate *priv = ide_transient_sidebar_get_instance_private (self);
+
+ g_assert (IDE_IS_TRANSIENT_SIDEBAR (self));
+ g_assert (IDE_IS_PAGE (page));
+
+ g_signal_handlers_disconnect_by_func (page,
+ G_CALLBACK (ide_transient_sidebar_page_destroyed),
+ self);
+
+ g_weak_ref_set (&priv->page_ref, NULL);
+
+ ide_transient_sidebar_after_set_focus (self, NULL, NULL);
+}
+
+static void
+ide_transient_sidebar_hierarchy_changed (GtkWidget *widget,
+ GtkWidget *old_toplevel)
+{
+ IdeTransientSidebar *self = (IdeTransientSidebar *)widget;
+ IdeTransientSidebarPrivate *priv = ide_transient_sidebar_get_instance_private (self);
+ GtkWidget *toplevel;
+
+ g_assert (IDE_IS_TRANSIENT_SIDEBAR (self));
+ g_assert (!old_toplevel || GTK_IS_WIDGET (old_toplevel));
+
+ toplevel = gtk_widget_get_toplevel (widget);
+ if (!GTK_IS_WINDOW (toplevel))
+ toplevel = NULL;
+
+ dzl_signal_group_set_target (priv->toplevel_signals, toplevel);
+}
+
+static void
+ide_transient_sidebar_finalize (GObject *object)
+{
+ IdeTransientSidebar *self = (IdeTransientSidebar *)object;
+ IdeTransientSidebarPrivate *priv = ide_transient_sidebar_get_instance_private (self);
+
+ g_clear_object (&priv->toplevel_signals);
+ g_weak_ref_clear (&priv->page_ref);
+
+ G_OBJECT_CLASS (ide_transient_sidebar_parent_class)->finalize (object);
+}
+
+static void
+ide_transient_sidebar_class_init (IdeTransientSidebarClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = ide_transient_sidebar_finalize;
+
+ widget_class->hierarchy_changed = ide_transient_sidebar_hierarchy_changed;
+}
+
+static void
+ide_transient_sidebar_init (IdeTransientSidebar *self)
+{
+ IdeTransientSidebarPrivate *priv = ide_transient_sidebar_get_instance_private (self);
+ GtkWidget *paned;
+ GtkWidget *stack;
+
+ g_weak_ref_init (&priv->page_ref, NULL);
+
+ priv->toplevel_signals = dzl_signal_group_new (GTK_TYPE_WINDOW);
+
+ dzl_signal_group_connect_data (priv->toplevel_signals,
+ "set-focus",
+ G_CALLBACK (ide_transient_sidebar_after_set_focus),
+ self, NULL,
+ G_CONNECT_AFTER | G_CONNECT_SWAPPED);
+
+ if (NULL != (paned = gtk_bin_get_child (GTK_BIN (self))) &&
+ DZL_IS_MULTI_PANED (paned) &&
+ NULL != (stack = dzl_multi_paned_get_nth_child (DZL_MULTI_PANED (paned), 0)) &&
+ DZL_IS_DOCK_STACK (stack))
+ {
+ GtkWidget *tab_strip;
+
+ /* We want to hide the tab strip in the stack for the transient bar */
+ tab_strip = dzl_gtk_widget_find_child_typed (stack, DZL_TYPE_TAB_STRIP);
+ if (tab_strip != NULL)
+ gtk_widget_hide (tab_strip);
+ }
+}
+
+/**
+ * ide_transient_sidebar_set_page:
+ * @self: a #IdeTransientSidebar
+ * @page: (nullable): An #IdePage or %NULL
+ *
+ * Sets the page for which the panel is transient for. When focus leaves the
+ * sidebar or the page, the panel will be dismissed.
+ *
+ * Since: 3.32
+ */
+void
+ide_transient_sidebar_set_page (IdeTransientSidebar *self,
+ IdePage *page)
+{
+ IdeTransientSidebarPrivate *priv = ide_transient_sidebar_get_instance_private (self);
+ g_autoptr(GtkWidget) old_page = NULL;
+
+ g_return_if_fail (IDE_IS_TRANSIENT_SIDEBAR (self));
+ g_return_if_fail (!page || IDE_IS_PAGE (page));
+
+ old_page = g_weak_ref_get (&priv->page_ref);
+ if (old_page != NULL)
+ g_signal_handlers_disconnect_by_func (old_page,
+ G_CALLBACK (ide_transient_sidebar_page_destroyed),
+ self);
+
+ if (page != NULL)
+ g_signal_connect_object (page,
+ "destroy",
+ G_CALLBACK (ide_transient_sidebar_page_destroyed),
+ self,
+ G_CONNECT_SWAPPED);
+
+ g_weak_ref_set (&priv->page_ref, page);
+}
+
+void
+ide_transient_sidebar_set_panel (IdeTransientSidebar *self,
+ GtkWidget *panel)
+{
+ GtkWidget *stack;
+
+ g_return_if_fail (IDE_IS_TRANSIENT_SIDEBAR (self));
+ g_return_if_fail (GTK_IS_WIDGET (panel));
+
+ stack = gtk_widget_get_parent (GTK_WIDGET (panel));
+
+ if (GTK_IS_STACK (stack))
+ gtk_stack_set_visible_child (GTK_STACK (stack), panel);
+ else
+ g_warning ("Failed to locate stack containing panel");
+}
+
+void
+ide_transient_sidebar_lock (IdeTransientSidebar *self)
+{
+ IdeTransientSidebarPrivate *priv = ide_transient_sidebar_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_TRANSIENT_SIDEBAR (self));
+ g_return_if_fail (priv->hold_count >= 0);
+
+ priv->hold_count++;
+
+ if (!dzl_dock_revealer_get_reveal_child (DZL_DOCK_REVEALER (self)))
+ set_visible (self, TRUE);
+}
+
+void
+ide_transient_sidebar_unlock (IdeTransientSidebar *self)
+{
+ IdeTransientSidebarPrivate *priv = ide_transient_sidebar_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_TRANSIENT_SIDEBAR (self));
+ g_return_if_fail (priv->hold_count > 0);
+
+ priv->hold_count--;
+
+ if (priv->hold_count == 0)
+ {
+ if (dzl_dock_revealer_get_reveal_child (DZL_DOCK_REVEALER (self)))
+ set_visible (self, FALSE);
+ }
+}
diff --git a/src/libide/gui/ide-transient-sidebar.h b/src/libide/gui/ide-transient-sidebar.h
new file mode 100644
index 000000000..0e27c0525
--- /dev/null
+++ b/src/libide/gui/ide-transient-sidebar.h
@@ -0,0 +1,58 @@
+/* ide-transient-sidebar.h
+ *
+ * Copyright 2017-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#if !defined (IDE_GUI_INSIDE) && !defined (IDE_GUI_COMPILATION)
+# error "Only <libide-gui.h> can be included directly."
+#endif
+
+#include <libide-core.h>
+
+#include "ide-panel.h"
+#include "ide-page.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_TRANSIENT_SIDEBAR (ide_transient_sidebar_get_type())
+
+IDE_AVAILABLE_IN_3_32
+G_DECLARE_DERIVABLE_TYPE (IdeTransientSidebar, ide_transient_sidebar, IDE, TRANSIENT_SIDEBAR, IdePanel)
+
+struct _IdeTransientSidebarClass
+{
+ IdePanelClass parent_class;
+
+ /*< private >*/
+ gpointer _reserved[16];
+};
+
+IDE_AVAILABLE_IN_3_32
+void ide_transient_sidebar_set_panel (IdeTransientSidebar *self,
+ GtkWidget *panel);
+IDE_AVAILABLE_IN_3_32
+void ide_transient_sidebar_set_page (IdeTransientSidebar *self,
+ IdePage *page);
+IDE_AVAILABLE_IN_3_32
+void ide_transient_sidebar_lock (IdeTransientSidebar *self);
+IDE_AVAILABLE_IN_3_32
+void ide_transient_sidebar_unlock (IdeTransientSidebar *self);
+
+G_END_DECLS
diff --git a/src/libide/util/ide-window-settings.h b/src/libide/gui/ide-window-settings-private.h
similarity index 93%
rename from src/libide/util/ide-window-settings.h
rename to src/libide/gui/ide-window-settings-private.h
index f4ef33115..e8fecd2b4 100644
--- a/src/libide/util/ide-window-settings.h
+++ b/src/libide/gui/ide-window-settings-private.h
@@ -24,6 +24,6 @@
G_BEGIN_DECLS
-void ide_window_settings_register (GtkWindow *window);
+void _ide_window_settings_register (GtkWindow *window);
G_END_DECLS
diff --git a/src/libide/util/ide-window-settings.c b/src/libide/gui/ide-window-settings.c
similarity index 96%
rename from src/libide/util/ide-window-settings.c
rename to src/libide/gui/ide-window-settings.c
index 63a336f39..dc2701c48 100644
--- a/src/libide/util/ide-window-settings.c
+++ b/src/libide/gui/ide-window-settings.c
@@ -18,7 +18,11 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
-#include "util/ide-window-settings.h"
+#define G_LOG_DOMAIN "ide-window-settings"
+
+#include "config.h"
+
+#include "ide-window-settings-private.h"
#define GB_WINDOW_MIN_WIDTH 1280
#define GB_WINDOW_MIN_HEIGHT 720
@@ -132,7 +136,7 @@ ide_window_settings__window_destroy (GtkWindow *window)
}
void
-ide_window_settings_register (GtkWindow *window)
+_ide_window_settings_register (GtkWindow *window)
{
if (settings == NULL)
{
diff --git a/src/libide/gui/ide-workbench-addin.c b/src/libide/gui/ide-workbench-addin.c
new file mode 100644
index 000000000..3876fe118
--- /dev/null
+++ b/src/libide/gui/ide-workbench-addin.c
@@ -0,0 +1,402 @@
+/* ide-workbench-addin.c
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-workbench-addin"
+
+#include "config.h"
+
+#include "ide-workbench-addin.h"
+
+G_DEFINE_INTERFACE (IdeWorkbenchAddin, ide_workbench_addin, G_TYPE_OBJECT)
+
+static void ide_workbench_addin_real_open_at_async (IdeWorkbenchAddin *self,
+ GFile *file,
+ const gchar *hint,
+ gint at_line,
+ gint at_line_offset,
+ IdeBufferOpenFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+static void ide_workbench_addin_real_open_async (IdeWorkbenchAddin *self,
+ GFile *file,
+ const gchar *hint,
+ IdeBufferOpenFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+static void
+ide_workbench_addin_real_load_project_async (IdeWorkbenchAddin *self,
+ IdeProjectInfo *project_info,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ ide_task_report_new_error (self, callback, user_data,
+ ide_workbench_addin_real_load_project_async,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ "Loading projects is not supported");
+}
+
+static gboolean
+ide_workbench_addin_real_load_project_finish (IdeWorkbenchAddin *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ return ide_task_propagate_boolean (IDE_TASK (result), error);
+}
+
+static void
+ide_workbench_addin_real_unload_project_async (IdeWorkbenchAddin *self,
+ IdeProjectInfo *project_info,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ ide_task_report_new_error (self, callback, user_data,
+ ide_workbench_addin_real_unload_project_async,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ "Unloading projects is not supported");
+}
+
+static gboolean
+ide_workbench_addin_real_unload_project_finish (IdeWorkbenchAddin *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ return ide_task_propagate_boolean (IDE_TASK (result), error);
+}
+
+static void
+ide_workbench_addin_real_open_async (IdeWorkbenchAddin *self,
+ GFile *file,
+ const gchar *hint,
+ IdeBufferOpenFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ IdeWorkbenchAddinInterface *iface;
+
+ g_assert (IDE_IS_WORKBENCH_ADDIN (self));
+ g_assert (G_IS_FILE (file));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ iface = IDE_WORKBENCH_ADDIN_GET_IFACE (self);
+
+ if (iface->open_at_async == (gpointer)ide_workbench_addin_real_open_at_async)
+ {
+ ide_task_report_new_error (self, callback, user_data,
+ ide_workbench_addin_real_open_async,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ "Opening files is not supported");
+ return;
+ }
+
+ iface->open_at_async (self, file, hint, -1, -1, flags, cancellable, callback, user_data);
+}
+
+static void
+ide_workbench_addin_real_open_at_async (IdeWorkbenchAddin *self,
+ GFile *file,
+ const gchar *hint,
+ gint at_line,
+ gint at_line_offset,
+ IdeBufferOpenFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ IdeWorkbenchAddinInterface *iface;
+
+ g_assert (IDE_IS_WORKBENCH_ADDIN (self));
+ g_assert (G_IS_FILE (file));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ iface = IDE_WORKBENCH_ADDIN_GET_IFACE (self);
+
+ if (iface->open_async == (gpointer)ide_workbench_addin_real_open_async)
+ {
+ ide_task_report_new_error (self, callback, user_data,
+ ide_workbench_addin_real_open_at_async,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ "Opening files is not supported");
+ return;
+ }
+
+ iface->open_async (self, file, hint, flags, cancellable, callback, user_data);
+}
+
+static gboolean
+ide_workbench_addin_real_open_finish (IdeWorkbenchAddin *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ return ide_task_propagate_boolean (IDE_TASK (result), error);
+}
+
+static void
+ide_workbench_addin_default_init (IdeWorkbenchAddinInterface *iface)
+{
+ iface->load_project_async = ide_workbench_addin_real_load_project_async;
+ iface->load_project_finish = ide_workbench_addin_real_load_project_finish;
+ iface->unload_project_async = ide_workbench_addin_real_unload_project_async;
+ iface->unload_project_finish = ide_workbench_addin_real_unload_project_finish;
+ iface->open_async = ide_workbench_addin_real_open_async;
+ iface->open_at_async = ide_workbench_addin_real_open_at_async;
+ iface->open_finish = ide_workbench_addin_real_open_finish;
+}
+
+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));
+
+ if (IDE_WORKBENCH_ADDIN_GET_IFACE (self)->load)
+ IDE_WORKBENCH_ADDIN_GET_IFACE (self)->load (self, workbench);
+}
+
+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));
+
+ if (IDE_WORKBENCH_ADDIN_GET_IFACE (self)->unload)
+ IDE_WORKBENCH_ADDIN_GET_IFACE (self)->unload (self, workbench);
+}
+
+void
+ide_workbench_addin_load_project_async (IdeWorkbenchAddin *self,
+ IdeProjectInfo *project_info,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (IDE_IS_WORKBENCH_ADDIN (self));
+ g_return_if_fail (IDE_IS_PROJECT_INFO (project_info));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ IDE_WORKBENCH_ADDIN_GET_IFACE (self)->load_project_async (self,
+ project_info,
+ cancellable,
+ callback,
+ user_data);
+}
+
+gboolean
+ide_workbench_addin_load_project_finish (IdeWorkbenchAddin *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (IDE_IS_WORKBENCH_ADDIN (self), FALSE);
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+ return IDE_WORKBENCH_ADDIN_GET_IFACE (self)->load_project_finish (self, result, error);
+}
+
+void
+ide_workbench_addin_unload_project_async (IdeWorkbenchAddin *self,
+ IdeProjectInfo *project_info,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (IDE_IS_WORKBENCH_ADDIN (self));
+ g_return_if_fail (IDE_IS_PROJECT_INFO (project_info));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ IDE_WORKBENCH_ADDIN_GET_IFACE (self)->unload_project_async (self,
+ project_info,
+ cancellable,
+ callback,
+ user_data);
+}
+
+gboolean
+ide_workbench_addin_unload_project_finish (IdeWorkbenchAddin *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (IDE_IS_WORKBENCH_ADDIN (self), FALSE);
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+ return IDE_WORKBENCH_ADDIN_GET_IFACE (self)->unload_project_finish (self, result, error);
+}
+
+void
+ide_workbench_addin_workspace_added (IdeWorkbenchAddin *self,
+ IdeWorkspace *workspace)
+{
+ g_return_if_fail (IDE_IS_WORKBENCH_ADDIN (self));
+ g_return_if_fail (IDE_IS_WORKSPACE (workspace));
+
+ if (IDE_WORKBENCH_ADDIN_GET_IFACE (self)->workspace_added)
+ IDE_WORKBENCH_ADDIN_GET_IFACE (self)->workspace_added (self, workspace);
+}
+
+void
+ide_workbench_addin_workspace_removed (IdeWorkbenchAddin *self,
+ IdeWorkspace *workspace)
+{
+ g_return_if_fail (IDE_IS_WORKBENCH_ADDIN (self));
+ g_return_if_fail (IDE_IS_WORKSPACE (workspace));
+
+ if (IDE_WORKBENCH_ADDIN_GET_IFACE (self)->workspace_removed)
+ IDE_WORKBENCH_ADDIN_GET_IFACE (self)->workspace_removed (self, workspace);
+}
+
+gboolean
+ide_workbench_addin_can_open (IdeWorkbenchAddin *self,
+ GFile *file,
+ const gchar *content_type,
+ gint *priority)
+{
+ gint real_priority;
+
+ g_return_val_if_fail (IDE_IS_WORKBENCH_ADDIN (self), FALSE);
+ g_return_val_if_fail (G_IS_FILE (file), FALSE);
+
+ if (priority == NULL)
+ priority = &real_priority;
+ else
+ *priority = 0;
+
+ if (IDE_WORKBENCH_ADDIN_GET_IFACE (self)->can_open)
+ return IDE_WORKBENCH_ADDIN_GET_IFACE (self)->can_open (self, file, content_type, priority);
+
+ return FALSE;
+}
+
+void
+ide_workbench_addin_open_async (IdeWorkbenchAddin *self,
+ GFile *file,
+ const gchar *content_type,
+ IdeBufferOpenFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (IDE_IS_WORKBENCH_ADDIN (self));
+ g_return_if_fail (G_IS_FILE (file));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ IDE_WORKBENCH_ADDIN_GET_IFACE (self)->open_async (self,
+ file,
+ content_type,
+ flags,
+ cancellable,
+ callback,
+ user_data);
+}
+
+void
+ide_workbench_addin_open_at_async (IdeWorkbenchAddin *self,
+ GFile *file,
+ const gchar *content_type,
+ gint at_line,
+ gint at_line_offset,
+ IdeBufferOpenFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (IDE_IS_WORKBENCH_ADDIN (self));
+ g_return_if_fail (G_IS_FILE (file));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ IDE_WORKBENCH_ADDIN_GET_IFACE (self)->open_at_async (self,
+ file,
+ content_type,
+ at_line,
+ at_line_offset,
+ flags,
+ cancellable,
+ callback,
+ user_data);
+}
+
+gboolean
+ide_workbench_addin_open_finish (IdeWorkbenchAddin *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (IDE_IS_WORKBENCH_ADDIN (self), FALSE);
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+ return IDE_WORKBENCH_ADDIN_GET_IFACE (self)->open_finish (self, result, error);
+}
+
+/**
+ * ide_workbench_addin_vcs_changed:
+ * @self: a #IdeWorkbenchAddin
+ * @vcs: (nullable): an #IdeVcs
+ *
+ * This function notifies an #IdeWorkbenchAddin that the version control
+ * system has changed. This happens when ide_workbench_set_vcs() is called
+ * or after an addin is loaded.
+ *
+ * This is helpful for plugins that want to react to VCS changes such as
+ * changing branches, or tracking commits.
+ *
+ * Since: 3.32
+ */
+void
+ide_workbench_addin_vcs_changed (IdeWorkbenchAddin *self,
+ IdeVcs *vcs)
+{
+ g_return_if_fail (IDE_IS_WORKBENCH_ADDIN (self));
+ g_return_if_fail (IDE_IS_VCS (vcs));
+
+ if (IDE_WORKBENCH_ADDIN_GET_IFACE (self)->vcs_changed)
+ IDE_WORKBENCH_ADDIN_GET_IFACE (self)->vcs_changed (self, vcs);
+}
+
+/**
+ * ide_workbench_addin_project_loaded:
+ * @self: an #IdeWorkbenchAddin
+ * @project_info: an #IdeProjectInfo
+ *
+ * This function is called after the project has been loaded.
+ *
+ * It is useful for situations where you do not need to influence the
+ * project loading, but do need to perform operations after it has
+ * completed.
+ *
+ * Since: 3.32
+ */
+void
+ide_workbench_addin_project_loaded (IdeWorkbenchAddin *self,
+ IdeProjectInfo *project_info)
+{
+ g_return_if_fail (IDE_IS_WORKBENCH_ADDIN (self));
+ g_return_if_fail (IDE_IS_PROJECT_INFO (project_info));
+
+ if (IDE_WORKBENCH_ADDIN_GET_IFACE (self)->project_loaded)
+ IDE_WORKBENCH_ADDIN_GET_IFACE (self)->project_loaded (self, project_info);
+}
diff --git a/src/libide/gui/ide-workbench-addin.h b/src/libide/gui/ide-workbench-addin.h
new file mode 100644
index 000000000..ec7f3db3f
--- /dev/null
+++ b/src/libide/gui/ide-workbench-addin.h
@@ -0,0 +1,159 @@
+/* ide-workbench-addin.h
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include "ide-workbench.h"
+#include "ide-workspace.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_WORKBENCH_ADDIN (ide_workbench_addin_get_type ())
+
+IDE_AVAILABLE_IN_3_32
+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 (*load_project_async) (IdeWorkbenchAddin *self,
+ IdeProjectInfo *project_info,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (*load_project_finish) (IdeWorkbenchAddin *self,
+ GAsyncResult *result,
+ GError **error);
+ void (*unload_project_async) (IdeWorkbenchAddin *self,
+ IdeProjectInfo *project_info,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (*unload_project_finish) (IdeWorkbenchAddin *self,
+ GAsyncResult *result,
+ GError **error);
+ void (*project_loaded) (IdeWorkbenchAddin *self,
+ IdeProjectInfo *project_info);
+ void (*workspace_added) (IdeWorkbenchAddin *self,
+ IdeWorkspace *workspace);
+ void (*workspace_removed) (IdeWorkbenchAddin *self,
+ IdeWorkspace *workspace);
+ gboolean (*can_open) (IdeWorkbenchAddin *self,
+ GFile *file,
+ const gchar *content_type,
+ gint *priority);
+ void (*open_async) (IdeWorkbenchAddin *self,
+ GFile *file,
+ const gchar *content_type,
+ IdeBufferOpenFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ void (*open_at_async) (IdeWorkbenchAddin *self,
+ GFile *file,
+ const gchar *content_type,
+ gint at_line,
+ gint at_line_offset,
+ IdeBufferOpenFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (*open_finish) (IdeWorkbenchAddin *self,
+ GAsyncResult *result,
+ GError **error);
+ void (*vcs_changed) (IdeWorkbenchAddin *self,
+ IdeVcs *vcs);
+};
+
+IDE_AVAILABLE_IN_3_32
+void ide_workbench_addin_load (IdeWorkbenchAddin *self,
+ IdeWorkbench *workbench);
+IDE_AVAILABLE_IN_3_32
+void ide_workbench_addin_unload (IdeWorkbenchAddin *self,
+ IdeWorkbench *workbench);
+IDE_AVAILABLE_IN_3_32
+void ide_workbench_addin_load_project_async (IdeWorkbenchAddin *self,
+ IdeProjectInfo *project_info,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+IDE_AVAILABLE_IN_3_32
+gboolean ide_workbench_addin_load_project_finish (IdeWorkbenchAddin *self,
+ GAsyncResult *result,
+ GError **error);
+IDE_AVAILABLE_IN_3_32
+void ide_workbench_addin_unload_project_async (IdeWorkbenchAddin *self,
+ IdeProjectInfo *project_info,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+IDE_AVAILABLE_IN_3_32
+gboolean ide_workbench_addin_unload_project_finish (IdeWorkbenchAddin *self,
+ GAsyncResult *result,
+ GError **error);
+IDE_AVAILABLE_IN_3_32
+void ide_workbench_addin_project_loaded (IdeWorkbenchAddin *self,
+ IdeProjectInfo *project_info);
+IDE_AVAILABLE_IN_3_32
+void ide_workbench_addin_workspace_added (IdeWorkbenchAddin *self,
+ IdeWorkspace *workspace);
+IDE_AVAILABLE_IN_3_32
+void ide_workbench_addin_workspace_removed (IdeWorkbenchAddin *self,
+ IdeWorkspace *workspace);
+IDE_AVAILABLE_IN_3_32
+gboolean ide_workbench_addin_can_open (IdeWorkbenchAddin *self,
+ GFile *file,
+ const gchar *content_type,
+ gint *priority);
+IDE_AVAILABLE_IN_3_32
+void ide_workbench_addin_open_async (IdeWorkbenchAddin *self,
+ GFile *file,
+ const gchar *content_type,
+ IdeBufferOpenFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+IDE_AVAILABLE_IN_3_32
+void ide_workbench_addin_open_at_async (IdeWorkbenchAddin *self,
+ GFile *file,
+ const gchar *content_type,
+ gint at_line,
+ gint at_line_offset,
+ IdeBufferOpenFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+IDE_AVAILABLE_IN_3_32
+gboolean ide_workbench_addin_open_finish (IdeWorkbenchAddin *self,
+ GAsyncResult *result,
+ GError **error);
+IDE_AVAILABLE_IN_3_32
+void ide_workbench_addin_vcs_changed (IdeWorkbenchAddin *self,
+ IdeVcs *vcs);
+IDE_AVAILABLE_IN_3_32
+IdeWorkbenchAddin *ide_workbench_addin_find_by_module_name (IdeWorkbench *workbench,
+ const gchar *module_name);
+
+G_END_DECLS
diff --git a/src/libide/gui/ide-workbench.c b/src/libide/gui/ide-workbench.c
new file mode 100644
index 000000000..ddd963959
--- /dev/null
+++ b/src/libide/gui/ide-workbench.c
@@ -0,0 +1,2299 @@
+/* ide-workbench.c
+ *
+ * Copyright 2014-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-workbench"
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <libide-debugger.h>
+#include <libide-threading.h>
+#include <libpeas/peas.h>
+
+#include "ide-context-private.h"
+#include "ide-foundry-init.h"
+#include "ide-thread-private.h"
+
+#include "ide-application.h"
+#include "ide-gui-global.h"
+#include "ide-gui-private.h"
+#include "ide-primary-workspace.h"
+#include "ide-session-private.h"
+#include "ide-workbench.h"
+#include "ide-workbench-addin.h"
+#include "ide-workspace.h"
+
+/**
+ * SECTION:ide-workbench
+ * @title: IdeWorkbench
+ * @short_description: window group for all windows within a project
+ *
+ * The #IdeWorkbench is a #GtkWindowGroup containing the #IdeContext (root
+ * data-structure for a project) and all of the windows associated with the
+ * project.
+ *
+ * Usually, windows within the #IdeWorkbench are an #IdeWorkspace. They can
+ * react to changes in the #IdeContext or its descendants to represent the
+ * project and it's state.
+ *
+ * Since: 3.32
+ */
+
+struct _IdeWorkbench
+{
+ GtkWindowGroup parent_instance;
+
+ /* MRU of workspaces, link embedded in workspace */
+ GQueue mru_queue;
+
+ /* Owned references */
+ PeasExtensionSet *addins;
+ GCancellable *cancellable;
+ IdeContext *context;
+ IdeBuildSystem *build_system;
+ IdeProjectInfo *project_info;
+ IdeVcs *vcs;
+ IdeVcsMonitor *vcs_monitor;
+ IdeSearchEngine *search_engine;
+ IdeSession *session;
+
+ /* Various flags */
+ guint unloaded : 1;
+};
+
+typedef struct
+{
+ GPtrArray *addins;
+ IdeWorkbenchAddin *preferred;
+ GFile *file;
+ gchar *hint;
+ gchar *content_type;
+ IdeBufferOpenFlags flags;
+ gint at_line;
+ gint at_line_offset;
+} Open;
+
+typedef struct
+{
+ IdeProjectInfo *project_info;
+ GPtrArray *addins;
+ GType workspace_type;
+ gint64 present_time;
+} LoadProject;
+
+enum {
+ PROP_0,
+ PROP_CONTEXT,
+ PROP_VCS,
+ N_PROPS
+};
+
+static void ide_workbench_action_close (IdeWorkbench *self,
+ GVariant *param);
+static void ide_workbench_action_open (IdeWorkbench *self,
+ GVariant *param);
+static void ide_workbench_action_dump_tasks (IdeWorkbench *self,
+ GVariant *param);
+static void ide_workbench_action_object_tree (IdeWorkbench *self,
+ GVariant *param);
+static void ide_workbench_action_inspector (IdeWorkbench *self,
+ GVariant *param);
+
+
+DZL_DEFINE_ACTION_GROUP (IdeWorkbench, ide_workbench, {
+ { "close", ide_workbench_action_close },
+ { "open", ide_workbench_action_open },
+ { "-inspector", ide_workbench_action_inspector },
+ { "-object-tree", ide_workbench_action_object_tree },
+ { "-dump-tasks", ide_workbench_action_dump_tasks },
+})
+
+G_DEFINE_TYPE_WITH_CODE (IdeWorkbench, ide_workbench, GTK_TYPE_WINDOW_GROUP,
+ G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP,
+ ide_workbench_init_action_group))
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+load_project_free (LoadProject *lp)
+{
+ g_clear_object (&lp->project_info);
+ g_clear_pointer (&lp->addins, g_ptr_array_unref);
+ g_slice_free (LoadProject, lp);
+}
+
+static void
+open_free (Open *o)
+{
+ g_clear_pointer (&o->addins, g_ptr_array_unref);
+ g_clear_object (&o->preferred);
+ g_clear_object (&o->file);
+ g_clear_pointer (&o->hint, g_free);
+ g_clear_pointer (&o->content_type, g_free);
+ g_slice_free (Open, o);
+}
+
+static gboolean
+ignore_error (GError *error)
+{
+ return g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
+ g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
+}
+
+static void
+ide_workbench_set_context (IdeWorkbench *self,
+ IdeContext *context)
+{
+ g_autoptr(IdeContext) new_context = NULL;
+ g_autoptr(IdeBufferManager) bufmgr = NULL;
+ IdeBuildSystem *build_system;
+
+ g_return_if_fail (IDE_IS_WORKBENCH (self));
+ g_return_if_fail (!context || IDE_IS_CONTEXT (context));
+
+ if (context == NULL)
+ context = new_context = ide_context_new ();
+
+ g_set_object (&self->context, context);
+
+ /* Make sure we have access to buffer manager early */
+ bufmgr = ide_object_ensure_child_typed (IDE_OBJECT (context), IDE_TYPE_BUFFER_MANAGER);
+
+ /* And use a fallback build system if one is not already available */
+ if ((build_system = ide_context_peek_child_typed (context, IDE_TYPE_BUILD_SYSTEM)))
+ self->build_system = g_object_ref (build_system);
+ else
+ self->build_system = ide_object_ensure_child_typed (IDE_OBJECT (context),
IDE_TYPE_FALLBACK_BUILD_SYSTEM);
+
+ /* Setup session monitor for future use */
+ self->session = ide_session_new ();
+ ide_object_append (IDE_OBJECT (self->context), IDE_OBJECT (self->session));
+}
+
+static void
+ide_workbench_addin_added_workspace_cb (IdeWorkspace *workspace,
+ IdeWorkbenchAddin *addin)
+{
+ g_assert (IDE_IS_WORKSPACE (workspace));
+ g_assert (IDE_IS_WORKBENCH_ADDIN (addin));
+
+ ide_workbench_addin_workspace_added (addin, workspace);
+}
+
+static void
+ide_workbench_addin_removed_workspace_cb (IdeWorkspace *workspace,
+ IdeWorkbenchAddin *addin)
+{
+ g_assert (IDE_IS_WORKSPACE (workspace));
+ g_assert (IDE_IS_WORKBENCH_ADDIN (addin));
+
+ ide_workbench_addin_workspace_removed (addin, workspace);
+}
+
+static void
+ide_workbench_addin_added_cb (PeasExtensionSet *set,
+ PeasPluginInfo *plugin_info,
+ PeasExtension *exten,
+ gpointer user_data)
+{
+ IdeWorkbench *self = user_data;
+ IdeWorkbenchAddin *addin = (IdeWorkbenchAddin *)exten;
+
+ g_assert (PEAS_IS_EXTENSION_SET (set));
+ g_assert (plugin_info != NULL);
+ g_assert (IDE_IS_WORKBENCH_ADDIN (addin));
+ g_assert (IDE_IS_WORKBENCH (self));
+
+ ide_workbench_addin_load (addin, self);
+
+ /* Notify of the VCS system up-front */
+ if (self->vcs != NULL)
+ ide_workbench_addin_vcs_changed (addin, self->vcs);
+
+ /*
+ * If we already loaded a project, then give the plugin a
+ * chance to handle that, even if it is delayed a bit.
+ */
+
+ if (self->project_info != NULL)
+ ide_workbench_addin_load_project_async (addin, self->project_info, NULL, NULL, NULL);
+
+ ide_workbench_foreach_workspace (self,
+ (GtkCallback)ide_workbench_addin_added_workspace_cb,
+ addin);
+}
+
+static void
+ide_workbench_addin_removed_cb (PeasExtensionSet *set,
+ PeasPluginInfo *plugin_info,
+ PeasExtension *exten,
+ gpointer user_data)
+{
+ IdeWorkbench *self = user_data;
+ IdeWorkbenchAddin *addin = (IdeWorkbenchAddin *)exten;
+
+ g_assert (PEAS_IS_EXTENSION_SET (set));
+ g_assert (plugin_info != NULL);
+ g_assert (IDE_IS_WORKBENCH_ADDIN (addin));
+ g_assert (IDE_IS_WORKBENCH (self));
+
+ /* Notify of workspace removals so addins don't need to manually
+ * track them for cleanup.
+ */
+ ide_workbench_foreach_workspace (self,
+ (GtkCallback)ide_workbench_addin_removed_workspace_cb,
+ addin);
+
+ ide_workbench_addin_unload (addin, self);
+}
+
+static void
+ide_workbench_notify_context_title (IdeWorkbench *self,
+ GParamSpec *pspec,
+ IdeContext *context)
+{
+ g_autofree gchar *formatted = NULL;
+ g_autofree gchar *title = NULL;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_WORKBENCH (self));
+ g_assert (IDE_IS_CONTEXT (context));
+
+ title = ide_context_dup_title (context);
+ formatted = g_strdup_printf (_("Builder — %s"), title);
+ ide_workbench_foreach_workspace (self,
+ (GtkCallback)gtk_window_set_title,
+ formatted);
+}
+
+static void
+ide_workbench_notify_context_workdir (IdeWorkbench *self,
+ GParamSpec *pspec,
+ IdeContext *context)
+{
+ g_autoptr(GFile) workdir = NULL;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_WORKBENCH (self));
+ g_assert (IDE_IS_CONTEXT (context));
+
+ workdir = ide_context_ref_workdir (context);
+ ide_vcs_monitor_set_root (self->vcs_monitor, workdir);
+}
+
+static void
+ide_workbench_constructed (GObject *object)
+{
+ IdeWorkbench *self = (IdeWorkbench *)object;
+
+ g_assert (IDE_IS_WORKBENCH (self));
+
+ if (self->context == NULL)
+ self->context = ide_context_new ();
+
+ g_signal_connect_object (self->context,
+ "notify::title",
+ G_CALLBACK (ide_workbench_notify_context_title),
+ self,
+ G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (self->context,
+ "notify::workdir",
+ G_CALLBACK (ide_workbench_notify_context_workdir),
+ self,
+ G_CONNECT_SWAPPED);
+
+ G_OBJECT_CLASS (ide_workbench_parent_class)->constructed (object);
+
+ self->vcs_monitor = g_object_new (IDE_TYPE_VCS_MONITOR, NULL);
+ ide_object_append (IDE_OBJECT (self->context), IDE_OBJECT (self->vcs_monitor));
+
+ 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_cb),
+ self);
+
+ g_signal_connect (self->addins,
+ "extension-removed",
+ G_CALLBACK (ide_workbench_addin_removed_cb),
+ self);
+
+ peas_extension_set_foreach (self->addins,
+ ide_workbench_addin_added_cb,
+ self);
+}
+
+static void
+ide_workbench_finalize (GObject *object)
+{
+ IdeWorkbench *self = (IdeWorkbench *)object;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+
+ g_clear_object (&self->build_system);
+ g_clear_object (&self->vcs);
+ g_clear_object (&self->search_engine);
+ g_clear_object (&self->session);
+ g_clear_object (&self->project_info);
+ g_clear_object (&self->cancellable);
+ g_clear_object (&self->context);
+
+ G_OBJECT_CLASS (ide_workbench_parent_class)->finalize (object);
+}
+
+static void
+ide_workbench_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdeWorkbench *self = IDE_WORKBENCH (object);
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+
+ switch (prop_id)
+ {
+ case PROP_CONTEXT:
+ g_value_set_object (value, ide_workbench_get_context (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);
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+
+ switch (prop_id)
+ {
+ case PROP_CONTEXT:
+ ide_workbench_set_context (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);
+
+ 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;
+
+ /**
+ * IdeWorkbench:context:
+ *
+ * The "context" property is the #IdeContext for the project.
+ *
+ * The #IdeContext is the root #IdeObject used in the tree of
+ * objects representing the project and the workings of the IDE.
+ *
+ * Since: 3.32
+ */
+ properties [PROP_CONTEXT] =
+ g_param_spec_object ("context",
+ "Context",
+ "The IdeContext for the workbench",
+ IDE_TYPE_CONTEXT,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * IdeWorkbench:vcs:
+ *
+ * The "vcs" property contains an #IdeVcs that represents the version control
+ * system that is currently loaded for the project.
+ *
+ * The #IdeVcs is registered by an #IdeWorkbenchAddin when loading a project.
+ *
+ * Since: 3.32
+ */
+ properties [PROP_VCS] =
+ g_param_spec_object ("vcs",
+ "Vcs",
+ "The version control system, if any",
+ IDE_TYPE_VCS,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+ide_workbench_init (IdeWorkbench *self)
+{
+}
+
+static void
+collect_addins_cb (PeasExtensionSet *set,
+ PeasPluginInfo *plugin_info,
+ PeasExtension *exten,
+ gpointer user_data)
+{
+ GPtrArray *ar = user_data;
+ g_ptr_array_add (ar, g_object_ref (exten));
+}
+
+static GPtrArray *
+ide_workbench_collect_addins (IdeWorkbench *self)
+{
+ g_autoptr(GPtrArray) ar = NULL;
+
+ g_assert (IDE_IS_WORKBENCH (self));
+
+ ar = g_ptr_array_new_with_free_func (g_object_unref);
+ if (self->addins != NULL)
+ peas_extension_set_foreach (self->addins, collect_addins_cb, ar);
+ return g_steal_pointer (&ar);
+}
+
+static IdeWorkbenchAddin *
+ide_workbench_find_addin (IdeWorkbench *self,
+ const gchar *hint)
+{
+ PeasEngine *engine;
+ PeasPluginInfo *plugin_info;
+ PeasExtension *exten = NULL;
+
+ g_return_val_if_fail (IDE_IS_WORKBENCH (self), NULL);
+ g_return_val_if_fail (hint != NULL, NULL);
+
+ engine = peas_engine_get_default ();
+
+ if ((plugin_info = peas_engine_get_plugin_info (engine, hint)))
+ exten = peas_extension_set_get_extension (self->addins, plugin_info);
+
+ return exten ? g_object_ref (IDE_WORKBENCH_ADDIN (exten)) : NULL;
+}
+
+/**
+ * ide_workbench_new:
+ *
+ * Creates a new #IdeWorkbench.
+ *
+ * This does not create any windows, you'll need to request that a workspace
+ * be created based on the kind of workspace you want to display to the user.
+ *
+ * Returns: an #IdeWorkbench
+ *
+ * Since: 3.32
+ */
+IdeWorkbench *
+ide_workbench_new (void)
+{
+ g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
+
+ return g_object_new (IDE_TYPE_WORKBENCH, NULL);
+}
+
+/**
+ * ide_workbench_new_for_context:
+ *
+ * Creates a new #IdeWorkbench using @context for the #IdeWorkbench:context.
+ *
+ * Returns: (transfer full): an #IdeWorkbench
+ *
+ * Since: 3.32
+ */
+IdeWorkbench *
+ide_workbench_new_for_context (IdeContext *context)
+{
+ g_return_val_if_fail (IDE_IS_CONTEXT (context), NULL);
+
+ return g_object_new (IDE_TYPE_CONTEXT,
+ "visible", TRUE,
+ NULL);
+}
+
+/**
+ * ide_workbench_get_context:
+ * @self: an #IdeWorkbench
+ *
+ * Gets the #IdeContext for the workbench.
+ *
+ * Returns: (transfer none): an #IdeContext
+ *
+ * Since: 3.32
+ */
+IdeContext *
+ide_workbench_get_context (IdeWorkbench *self)
+{
+ g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
+ g_return_val_if_fail (IDE_IS_WORKBENCH (self), NULL);
+
+ return self->context;
+}
+
+/**
+ * ide_workbench_from_widget:
+ * @widget: a #GtkWidget
+ *
+ * Finds the #IdeWorkbench associated with a widget.
+ *
+ * Returns: (nullable) (transfer none): an #IdeWorkbench or %NULL
+ *
+ * Since: 3.32
+ */
+IdeWorkbench *
+ide_workbench_from_widget (GtkWidget *widget)
+{
+ GtkWindowGroup *group;
+ GtkWidget *toplevel;
+
+ g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+
+ /*
+ * The workbench is a window group, and the workspaces belong to us. So we
+ * just need to get the toplevel window group property, and cast.
+ */
+
+ if ((toplevel = gtk_widget_get_toplevel (widget)) &&
+ GTK_IS_WINDOW (toplevel) &&
+ (group = gtk_window_get_group (GTK_WINDOW (toplevel))) &&
+ IDE_IS_WORKBENCH (group))
+ return IDE_WORKBENCH (group);
+
+ return NULL;
+}
+
+/**
+ * ide_workbench_foreach_workspace:
+ * @self: an #IdeWorkbench
+ * @callback: (scope call): a #GtkCallback to call for each #IdeWorkspace
+ * @user_data: user data for @callback
+ *
+ * Iterates the available workspaces in the workbench. Workspaces are iterated
+ * in most-recently-used order.
+ *
+ * Since: 3.32
+ */
+void
+ide_workbench_foreach_workspace (IdeWorkbench *self,
+ GtkCallback callback,
+ gpointer user_data)
+{
+ GList *copy;
+
+ g_return_if_fail (IDE_IS_MAIN_THREAD ());
+ g_return_if_fail (IDE_IS_WORKBENCH (self));
+ g_return_if_fail (callback != NULL);
+
+ /* Copy for re-entrancy safety */
+ copy = g_list_copy (self->mru_queue.head);
+
+ for (const GList *iter = copy; iter; iter = iter->next)
+ {
+ IdeWorkspace *workspace = iter->data;
+ g_assert (IDE_IS_WORKSPACE (workspace));
+ callback (iter->data, user_data);
+ }
+
+ g_list_free (copy);
+}
+
+/**
+ * ide_workbench_foreach_page:
+ * @self: a #IdeWorkbench
+ * @callback: (scope call): a callback to execute for each page
+ * @user_data: closure data for @callback
+ *
+ * Calls @callback for every page loaded in the workbench, by iterating
+ * workspaces in order of most-recently-used.
+ *
+ * Since: 3.32
+ */
+void
+ide_workbench_foreach_page (IdeWorkbench *self,
+ GtkCallback callback,
+ gpointer user_data)
+{
+ GList *copy;
+
+ g_return_if_fail (IDE_IS_WORKBENCH (self));
+ g_return_if_fail (callback != NULL);
+
+ /* Make a copy to be safe against auto-cleanup removals */
+ copy = g_list_copy (self->mru_queue.head);
+ for (const GList *iter = copy; iter; iter = iter->next)
+ {
+ IdeWorkspace *workspace = iter->data;
+ g_assert (IDE_IS_WORKSPACE (workspace));
+ ide_workspace_foreach_page (workspace, callback, user_data);
+ }
+ g_list_free (copy);
+}
+
+static void
+ide_workbench_workspace_has_toplevel_focus_cb (IdeWorkbench *self,
+ GParamSpec *pspec,
+ IdeWorkspace *workspace)
+{
+ g_assert (IDE_IS_WORKBENCH (self));
+ g_assert (IDE_IS_WORKSPACE (workspace));
+ g_assert (gtk_window_get_group (GTK_WINDOW (workspace)) == GTK_WINDOW_GROUP (self));
+
+ if (gtk_window_has_toplevel_focus (GTK_WINDOW (workspace)))
+ {
+ GList *mru_link = _ide_workspace_get_mru_link (workspace);
+
+ g_queue_unlink (&self->mru_queue, mru_link);
+
+ g_assert (mru_link->prev == NULL);
+ g_assert (mru_link->next == NULL);
+ g_assert (mru_link->data == (gpointer)workspace);
+
+ g_queue_push_head_link (&self->mru_queue, mru_link);
+ }
+}
+
+static void
+insert_action_groups_foreach_cb (IdeWorkspace *workspace,
+ gpointer user_data)
+{
+ IdeWorkbench *self = user_data;
+ struct {
+ const gchar *name;
+ GType child_type;
+ } groups[] = {
+ { "config-manager", IDE_TYPE_CONFIGURATION_MANAGER },
+ { "build-manager", IDE_TYPE_BUILD_MANAGER },
+ { "device-manager", IDE_TYPE_DEVICE_MANAGER },
+ { "run-manager", IDE_TYPE_RUN_MANAGER },
+ { "test-manager", IDE_TYPE_TEST_MANAGER },
+ };
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_WORKBENCH (self));
+ g_assert (IDE_IS_WORKSPACE (workspace));
+
+ for (guint i = 0; i < G_N_ELEMENTS (groups); i++)
+ {
+ IdeObject *child;
+
+ if ((child = ide_context_peek_child_typed (self->context, groups[i].child_type)))
+ gtk_widget_insert_action_group (GTK_WIDGET (workspace),
+ groups[i].name,
+ G_ACTION_GROUP (child));
+ }
+}
+
+/**
+ * ide_workbench_add_workspace:
+ * @self: an #IdeWorkbench
+ * @workspace: an #IdeWorkspace
+ *
+ * Adds @workspace to @workbench.
+ *
+ * Since: 3.32
+ */
+void
+ide_workbench_add_workspace (IdeWorkbench *self,
+ IdeWorkspace *workspace)
+{
+ g_autoptr(GPtrArray) addins = NULL;
+ g_autofree gchar *title = NULL;
+ g_autofree gchar *formatted = NULL;
+ GList *mru_link;
+
+ g_return_if_fail (IDE_IS_MAIN_THREAD ());
+ g_return_if_fail (IDE_IS_WORKBENCH (self));
+ g_return_if_fail (IDE_IS_WORKSPACE (workspace));
+
+ /* Now add the window to the workspace (which takes no reference, as the
+ * window will take a reference back to us.
+ */
+ if (gtk_window_get_group (GTK_WINDOW (workspace)) != GTK_WINDOW_GROUP (self))
+ gtk_window_group_add_window (GTK_WINDOW_GROUP (self), GTK_WINDOW (workspace));
+
+ g_assert (gtk_window_has_group (GTK_WINDOW (workspace)));
+ g_assert (gtk_window_get_group (GTK_WINDOW (workspace)) == GTK_WINDOW_GROUP (self));
+
+ /* Now place the workspace into our MRU tracking */
+ mru_link = _ide_workspace_get_mru_link (workspace);
+
+ if (gtk_window_has_toplevel_focus (GTK_WINDOW (workspace)))
+ g_queue_push_head_link (&self->mru_queue, mru_link);
+ else
+ g_queue_push_tail_link (&self->mru_queue, mru_link);
+
+ /* Update the context for the workspace, even if we're not loaded,
+ * this IdeContext will be updated later.
+ */
+ _ide_workspace_set_context (workspace, self->context);
+
+ /* This causes the workspace to get an additional reference to the group
+ * (which already happens from GtkWindow:group), but IdeWorkspace will
+ * remove itself in IdeWorkspace.destroy.
+ */
+ gtk_widget_insert_action_group (GTK_WIDGET (workspace),
+ "workbench",
+ G_ACTION_GROUP (self));
+
+ /* Give the workspace access to all the action groups of the context that
+ * might be useful for them to access (debug-manager, run-manager, etc).
+ */
+ if (self->project_info != NULL)
+ insert_action_groups_foreach_cb (workspace, self);
+
+ /* Track toplevel focus changes to maintain a most-recently-used queue. */
+ g_signal_connect_object (workspace,
+ "notify::has-toplevel-focus",
+ G_CALLBACK (ide_workbench_workspace_has_toplevel_focus_cb),
+ self,
+ G_CONNECT_SWAPPED);
+
+ /* Notify all the addins about the new workspace. */
+ if ((addins = ide_workbench_collect_addins (self)))
+ {
+ for (guint i = 0; i < addins->len; i++)
+ {
+ IdeWorkbenchAddin *addin = g_ptr_array_index (addins, i);
+ ide_workbench_addin_workspace_added (addin, workspace);
+ }
+ }
+
+ title = ide_context_dup_title (self->context);
+ formatted = g_strdup_printf (_("Builder — %s"), title);
+ gtk_window_set_title (GTK_WINDOW (workspace), formatted);
+}
+
+/**
+ * ide_workbench_remove_workspace:
+ * @self: an #IdeWorkbench
+ * @workspace: an #IdeWorkspace
+ *
+ * Removes @workspace from @workbench.
+ *
+ * Since: 3.32
+ */
+void
+ide_workbench_remove_workspace (IdeWorkbench *self,
+ IdeWorkspace *workspace)
+{
+ g_autoptr(GPtrArray) addins = NULL;
+ GList *list;
+ GList *mru_link;
+ guint count = 0;
+
+ g_return_if_fail (IDE_IS_MAIN_THREAD ());
+ g_return_if_fail (IDE_IS_WORKBENCH (self));
+ g_return_if_fail (IDE_IS_WORKSPACE (workspace));
+
+ /* Stop tracking MRU changes */
+ mru_link = _ide_workspace_get_mru_link (workspace);
+ g_queue_unlink (&self->mru_queue, mru_link);
+ g_signal_handlers_disconnect_by_func (workspace,
+ G_CALLBACK (ide_workbench_workspace_has_toplevel_focus_cb),
+ self);
+
+ /* Notify all the addins about losing the workspace. */
+ if ((addins = ide_workbench_collect_addins (self)))
+ {
+ for (guint i = 0; i < addins->len; i++)
+ {
+ IdeWorkbenchAddin *addin = g_ptr_array_index (addins, i);
+ ide_workbench_addin_workspace_removed (addin, workspace);
+ }
+ }
+
+ /* Clear our action group (which drops an additional back-reference) */
+ gtk_widget_insert_action_group (GTK_WIDGET (workspace), "workbench", NULL);
+
+ /* Only cleanup the group if it hasn't already been removed */
+ if (gtk_window_has_group (GTK_WINDOW (workspace)))
+ gtk_window_group_remove_window (GTK_WINDOW_GROUP (self), GTK_WINDOW (workspace));
+
+ /*
+ * If this is our last workspace being closed, then we want to
+ * try to cleanup the workbench and shut things down.
+ */
+
+ list = gtk_window_group_list_windows (GTK_WINDOW_GROUP (self));
+ for (const GList *iter = list; iter; iter = iter->next)
+ {
+ GtkWindow *window = iter->data;
+
+ if (IDE_IS_WORKSPACE (window) && workspace != IDE_WORKSPACE (window))
+ count++;
+ }
+ g_list_free (list);
+
+ /*
+ * If there are no more workspaces left, then we will want to also
+ * unload the workbench opportunistically, so that the application
+ * can exit cleanly.
+ */
+ if (count == 0 && self->unloaded == FALSE)
+ ide_workbench_unload_async (self, NULL, NULL, NULL);
+}
+
+/**
+ * ide_workbench_focus_workspace:
+ * @self: an #IdeWorkbench
+ * @workspace: an #IdeWorkspace
+ *
+ * Requests that @workspace be raised in the windows of @self, and
+ * displayed to the user.
+ *
+ * Since: 3.32
+ */
+void
+ide_workbench_focus_workspace (IdeWorkbench *self,
+ IdeWorkspace *workspace)
+{
+ g_return_if_fail (IDE_IS_MAIN_THREAD ());
+ g_return_if_fail (IDE_IS_WORKBENCH (self));
+ g_return_if_fail (IDE_IS_WORKSPACE (workspace));
+
+ ide_gtk_window_present (GTK_WINDOW (workspace));
+}
+
+static void
+ide_workbench_project_loaded_foreach_cb (PeasExtensionSet *set,
+ PeasPluginInfo *plugin_info,
+ PeasExtension *exten,
+ gpointer user_data)
+{
+ IdeWorkbenchAddin *addin = (IdeWorkbenchAddin *)exten;
+ IdeWorkbench *self = user_data;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (PEAS_IS_EXTENSION_SET (set));
+ g_assert (IDE_IS_WORKBENCH_ADDIN (addin));
+ g_assert (IDE_IS_WORKBENCH (self));
+ g_assert (IDE_IS_PROJECT_INFO (self->project_info));
+
+ ide_workbench_addin_project_loaded (addin, self->project_info);
+}
+
+static void
+ide_workbench_session_restore_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeSession *session = (IdeSession *)object;
+ g_autoptr(GError) error = NULL;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_SESSION (session));
+ g_assert (G_IS_ASYNC_RESULT (result));
+
+ if (!ide_session_restore_finish (session, result, &error))
+ g_warning ("%s", error->message);
+}
+
+static void
+ide_workbench_load_project_completed (IdeWorkbench *self,
+ IdeTask *task)
+{
+ LoadProject *lp;
+
+ g_assert (IDE_IS_WORKBENCH (self));
+ g_assert (IDE_IS_TASK (task));
+
+ lp = ide_task_get_task_data (task);
+
+ g_assert (lp != NULL);
+ g_assert (lp->addins != NULL);
+ g_assert (lp->addins->len == 0);
+
+ if (lp->workspace_type != G_TYPE_INVALID)
+ {
+ IdeWorkspace *workspace;
+
+ workspace = g_object_new (lp->workspace_type,
+ "application", IDE_APPLICATION_DEFAULT,
+ NULL);
+ ide_workbench_add_workspace (self, IDE_WORKSPACE (workspace));
+ gtk_window_present_with_time (GTK_WINDOW (workspace), lp->present_time);
+ }
+
+ /* Give workspaces access to the various GActionGroups */
+ ide_workbench_foreach_workspace (self,
+ (GtkCallback)insert_action_groups_foreach_cb,
+ self);
+
+ /* Notify addins that projects have loaded */
+ peas_extension_set_foreach (self->addins,
+ ide_workbench_project_loaded_foreach_cb,
+ self);
+
+ /* And now restore the user session, but don't block our task for
+ * it since the greeter is waiting on us.
+ */
+ ide_session_restore_async (self->session,
+ self,
+ ide_task_get_cancellable (task),
+ ide_workbench_session_restore_cb,
+ NULL);
+
+ ide_task_return_boolean (task, TRUE);
+}
+
+static void
+ide_workbench_load_project_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeWorkbenchAddin *addin = (IdeWorkbenchAddin *)object;
+ g_autoptr(IdeTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+ IdeWorkbench *self;
+ LoadProject *lp;
+
+ g_assert (IDE_IS_WORKBENCH_ADDIN (addin));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_TASK (task));
+
+ self = ide_task_get_source_object (task);
+ lp = ide_task_get_task_data (task);
+
+ g_assert (IDE_IS_WORKBENCH (self));
+ g_assert (lp != NULL);
+ g_assert (IDE_IS_PROJECT_INFO (lp->project_info));
+ g_assert (lp->addins != NULL);
+ g_assert (lp->addins->len > 0);
+
+ if (!ide_workbench_addin_load_project_finish (addin, result, &error))
+ {
+ if (!ignore_error (error))
+ g_warning ("%s addin failed to load project: %s",
+ G_OBJECT_TYPE_NAME (addin), error->message);
+ }
+
+ g_ptr_array_remove (lp->addins, addin);
+
+ if (lp->addins->len == 0)
+ ide_workbench_load_project_completed (self, task);
+}
+
+static void
+ide_workbench_init_foundry_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr(IdeTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+ IdeWorkbench *self;
+ GCancellable *cancellable;
+ LoadProject *lp;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_TASK (task));
+
+ if (!_ide_foundry_init_finish (result, &error))
+ g_critical ("Failed to initialize foundry: %s", error->message);
+
+ cancellable = ide_task_get_cancellable (task);
+ self = ide_task_get_source_object (task);
+ lp = ide_task_get_task_data (task);
+
+ g_assert (IDE_IS_WORKBENCH (self));
+ g_assert (lp != NULL);
+ g_assert (lp->addins != NULL);
+ g_assert (IDE_IS_PROJECT_INFO (lp->project_info));
+
+ /* Now, we need to notify all of the workbench addins that we're
+ * opening the project. Once they have all completed, we'll create the
+ * new workspace window and attach it. That saves us the work of
+ * rendering various frames of the during the intensive load process.
+ */
+
+
+ for (guint i = 0; i < lp->addins->len; i++)
+ {
+ IdeWorkbenchAddin *addin = g_ptr_array_index (lp->addins, i);
+
+ ide_workbench_addin_load_project_async (addin,
+ lp->project_info,
+ cancellable,
+ ide_workbench_load_project_cb,
+ g_object_ref (task));
+ }
+
+ if (lp->addins->len == 0)
+ ide_workbench_load_project_completed (self, task);
+}
+
+/**
+ * ide_workbench_load_project_async:
+ * @self: a #IdeWorkbench
+ * @project_info: an #IdeProjectInfo describing the project to open
+ * @cancellable: (nullable): a #GCancellable or %NULL
+ * @callback: (nullable): a #GAsyncReadyCallback to execute upon completion
+ * @user_data: user data for @callback
+ *
+ * Requests that a project be opened in the workbench.
+ *
+ * @project_info should contain enough information to discover and load the
+ * project. Depending on the various fields of the #IdeProjectInfo,
+ * different plugins may become active as part of loading the project.
+ *
+ * Note that this may only be called once for an #IdeWorkbench. If you need
+ * to open a second project, you need to create and register a second
+ * workbench first, and then open using that secondary workbench.
+ *
+ * @callback should call ide_workbench_load_project_finish() to obtain the
+ * result of the open request.
+ *
+ * Since: 3.32
+ */
+void
+ide_workbench_load_project_async (IdeWorkbench *self,
+ IdeProjectInfo *project_info,
+ GType workspace_type,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GPtrArray) addins = NULL;
+ g_autoptr(IdeTask) task = NULL;
+ g_autoptr(GFile) parent = NULL;
+ g_autofree gchar *name = NULL;
+ const gchar *project_id;
+ LoadProject *lp;
+ GFile *directory;
+
+ IDE_ENTRY;
+
+ g_return_if_fail (IDE_IS_MAIN_THREAD ());
+ g_return_if_fail (IDE_IS_WORKBENCH (self));
+ g_return_if_fail (IDE_IS_PROJECT_INFO (project_info));
+ g_return_if_fail (workspace_type != IDE_TYPE_WORKSPACE);
+ g_return_if_fail (workspace_type == G_TYPE_INVALID ||
+ g_type_is_a (workspace_type, IDE_TYPE_WORKSPACE));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+ g_return_if_fail (self->unloaded == FALSE);
+
+ task = ide_task_new (self, cancellable, callback, user_data);
+ ide_task_set_source_tag (task, ide_workbench_load_project_async);
+
+ if (self->project_info != NULL)
+ {
+ ide_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "Cannot load project, a project is already loaded");
+ IDE_EXIT;
+ }
+
+ _ide_context_set_has_project (self->context);
+
+ g_set_object (&self->project_info, project_info);
+
+ /* Update context project-id based on project-info */
+ if ((project_id = ide_project_info_get_id (project_info)))
+ {
+ g_autofree gchar *generated = ide_create_project_id (project_id);
+ ide_context_set_project_id (self->context, generated);
+ }
+
+ /*
+ * Track the directory root based on project info. If we didn't get a
+ * directory set, then take the parent of the project file.
+ */
+
+ if ((directory = ide_project_info_get_directory (project_info)))
+ {
+ ide_context_set_workdir (self->context, directory);
+ }
+ else
+ {
+ GFile *file = ide_project_info_get_file (project_info);
+
+ if (g_file_query_file_type (file, G_FILE_COPY_NOFOLLOW_SYMLINKS, NULL) == G_FILE_TYPE_DIRECTORY)
+ {
+ ide_context_set_workdir (self->context, file);
+ directory = file;
+ }
+ else
+ {
+ ide_context_set_workdir (self->context, (parent = g_file_get_parent (file)));
+ directory = parent;
+ }
+
+ ide_project_info_set_directory (project_info, directory);
+ }
+
+ g_assert (G_IS_FILE (directory));
+
+ name = g_file_get_basename (directory);
+ ide_context_set_title (self->context, name);
+
+ /* If there has not been a project name set, make the default matching
+ * the directory name. A plugin may update the name with more information
+ * based on .doap files, etc.
+ */
+ if (!ide_project_info_get_name (project_info))
+ ide_project_info_set_name (project_info, name);
+
+ /* Setup some information we're going to need later on when loading the
+ * individual workbench addins (and then creating the workspace).
+ */
+ lp = g_slice_new0 (LoadProject);
+ lp->project_info = g_object_ref (project_info);
+ /* HACK: Workaround for lack of last event time */
+ lp->present_time = g_get_monotonic_time () / 1000L;
+ lp->addins = ide_workbench_collect_addins (self);
+ lp->workspace_type = workspace_type;
+ ide_task_set_task_data (task, lp, load_project_free);
+
+ /*
+ * Before we load any addins, we want to register the Foundry subsystems
+ * such as the device manager, diagnostics engine, configurations, etc.
+ * This makes sure that we have some basics setup before addins load.
+ */
+ _ide_foundry_init_async (self->context,
+ cancellable,
+ ide_workbench_init_foundry_cb,
+ g_steal_pointer (&task));
+
+ IDE_EXIT;
+}
+
+/**
+ * ide_workbench_load_project_finish:
+ * @self: a #IdeWorkbench
+ *
+ * Completes an asynchronous request to open a project using
+ * ide_workbench_load_project_async().
+ *
+ * Returns: %TRUE if the project was successfully opened; otherwise %FALSE
+ * and @error is set.
+ *
+ * Since: 3.32
+ */
+gboolean
+ide_workbench_load_project_finish (IdeWorkbench *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (IDE_IS_MAIN_THREAD (), FALSE);
+ g_return_val_if_fail (IDE_IS_WORKBENCH (self), FALSE);
+ g_return_val_if_fail (IDE_IS_TASK (result), FALSE);
+
+ return ide_task_propagate_boolean (IDE_TASK (result), error);
+}
+
+static void
+print_object_tree (IdeObject *object,
+ gpointer depthptr)
+{
+ gint depth = GPOINTER_TO_INT (depthptr);
+ g_autofree gchar *space = g_strnfill (depth * 2, ' ');
+ g_autofree gchar *info = ide_object_repr (object);
+
+ g_print ("%s%s\n", space, info);
+ ide_object_foreach (object,
+ (GFunc)print_object_tree,
+ GINT_TO_POINTER (depth + 1));
+}
+
+static void
+ide_workbench_action_object_tree (IdeWorkbench *self,
+ GVariant *param)
+{
+ g_assert (IDE_IS_WORKBENCH (self));
+
+ print_object_tree (IDE_OBJECT (self->context), NULL);
+}
+
+static void
+ide_workbench_action_dump_tasks (IdeWorkbench *self,
+ GVariant *param)
+{
+ g_assert (IDE_IS_WORKBENCH (self));
+
+ _ide_dump_tasks ();
+}
+
+static void
+ide_workbench_action_inspector (IdeWorkbench *self,
+ GVariant *param)
+{
+ gtk_window_set_interactive_debugging (TRUE);
+}
+
+static void
+ide_workbench_action_close (IdeWorkbench *self,
+ GVariant *param)
+{
+ g_assert (IDE_IS_WORKBENCH (self));
+ g_assert (param == NULL);
+
+ if (self->unloaded == FALSE)
+ ide_workbench_unload_async (self, NULL, NULL, NULL);
+}
+
+static void
+ide_workbench_action_open (IdeWorkbench *self,
+ GVariant *param)
+{
+ GtkFileChooserNative *chooser;
+ IdeWorkspace *workspace;
+ gint ret;
+
+ g_assert (IDE_IS_WORKBENCH (self));
+ g_assert (param == NULL);
+
+ workspace = ide_workbench_get_current_workspace (self);
+
+ chooser = gtk_file_chooser_native_new (_("Open File…"),
+ GTK_WINDOW (workspace),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ _("Open"),
+ _("Cancel"));
+ gtk_native_dialog_set_modal (GTK_NATIVE_DIALOG (chooser), FALSE);
+ gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (chooser), FALSE);
+ gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (chooser), TRUE);
+
+ ret = gtk_native_dialog_run (GTK_NATIVE_DIALOG (chooser));
+
+ if (ret == GTK_RESPONSE_ACCEPT)
+ {
+ g_autoslist(GFile) files = gtk_file_chooser_get_files (GTK_FILE_CHOOSER (chooser));
+
+ for (const GSList *iter = files; iter; iter = iter->next)
+ {
+ GFile *file = iter->data;
+
+ g_assert (G_IS_FILE (file));
+
+ ide_workbench_open_async (self, file, NULL, 0, NULL, NULL, NULL);
+ }
+ }
+
+ gtk_native_dialog_destroy (GTK_NATIVE_DIALOG (chooser));
+}
+
+/**
+ * ide_workbench_get_search_engine:
+ * @self: a #IdeWorkbench
+ *
+ * Gets the search engine for the workbench, if any.
+ *
+ * Returns: (transfer none): an #IdeSearchEngine
+ *
+ * Since: 3.32
+ */
+IdeSearchEngine *
+ide_workbench_get_search_engine (IdeWorkbench *self)
+{
+ g_return_val_if_fail (IDE_IS_WORKBENCH (self), NULL);
+ g_return_val_if_fail (self->context != NULL, NULL);
+
+ if (self->search_engine == NULL)
+ self->search_engine = ide_object_ensure_child_typed (IDE_OBJECT (self->context),
+ IDE_TYPE_SEARCH_ENGINE);
+
+ return self->search_engine;
+}
+
+/**
+ * ide_workbench_get_project_info:
+ * @self: a #IdeWorkbench
+ *
+ * Gets the #IdeProjectInfo for the workbench, if a project has been or is
+ * currently, loading.
+ *
+ * Returns: (transfer none) (nullable): an #IdeProjectInfo or %NULL
+ *
+ * Since: 3.32
+ */
+IdeProjectInfo *
+ide_workbench_get_project_info (IdeWorkbench *self)
+{
+ g_return_val_if_fail (IDE_IS_WORKBENCH (self), NULL);
+
+ return self->project_info;
+}
+
+static void
+ide_workbench_unload_project_completed (IdeWorkbench *self,
+ IdeTask *task)
+{
+ g_assert (IDE_IS_WORKBENCH (self));
+ g_assert (IDE_IS_TASK (task));
+
+ g_clear_object (&self->addins);
+ ide_workbench_foreach_workspace (self, (GtkCallback)gtk_widget_destroy, NULL);
+
+ if (self->context != NULL)
+ {
+ ide_object_destroy (IDE_OBJECT (self->context));
+ g_clear_object (&self->context);
+ }
+
+ ide_task_return_boolean (task, TRUE);
+}
+
+static void
+ide_workbench_unload_project_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeWorkbenchAddin *addin = (IdeWorkbenchAddin *)object;
+ g_autoptr(IdeTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+ IdeWorkbench *self;
+ GPtrArray *addins;
+
+ g_assert (IDE_IS_WORKBENCH_ADDIN (addin));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_TASK (task));
+
+ self = ide_task_get_source_object (task);
+ addins = ide_task_get_task_data (task);
+
+ g_assert (IDE_IS_WORKBENCH (self));
+ g_assert (addins != NULL);
+ g_assert (addins->len > 0);
+
+ if (!ide_workbench_addin_unload_project_finish (addin, result, &error))
+ {
+ if (!ignore_error (error))
+ g_warning ("%s failed to unload project: %s",
+ G_OBJECT_TYPE_NAME (addin), error->message);
+ }
+
+ g_ptr_array_remove (addins, addin);
+
+ if (addins->len == 0)
+ ide_workbench_unload_project_completed (self, task);
+}
+
+static void
+ide_workbench_session_save_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeSession *session = (IdeSession *)object;
+ g_autoptr(IdeTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+ IdeWorkbench *self;
+ GPtrArray *addins;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_SESSION (session));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_TASK (task));
+
+ /* Not much we can display to the user, as we're tearing widgets down */
+ if (!ide_session_save_finish (session, result, &error))
+ g_warning ("%s", error->message);
+
+ /* Now we can request that each of our addins unload the project. */
+
+ self = ide_task_get_source_object (task);
+ addins = ide_task_get_task_data (task);
+
+ g_assert (IDE_IS_WORKBENCH (self));
+ g_assert (addins != NULL);
+
+ if (addins->len == 0)
+ {
+ ide_workbench_unload_project_completed (self, task);
+ return;
+ }
+
+ for (guint i = 0; i < addins->len; i++)
+ {
+ IdeWorkbenchAddin *addin = g_ptr_array_index (addins, i);
+
+ ide_workbench_addin_unload_project_async (addin,
+ self->project_info,
+ ide_task_get_cancellable (task),
+ ide_workbench_unload_project_cb,
+ g_object_ref (task));
+ }
+}
+
+/**
+ * ide_workbench_unload_async:
+ * @self: an #IdeWorkbench
+ * @cancellable: (nullable): a #GCancellable
+ * @callback: a #GAsyncReadyCallback to execute upon completion
+ * @user_data: closure data for @callback
+ *
+ * Asynchronously unloads the workbench.
+ *
+ * All #IdeWorkspace windows will be closed after calling this
+ * function.
+ *
+ * Since: 3.32
+ */
+void
+ide_workbench_unload_async (IdeWorkbench *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(IdeTask) task = NULL;
+ g_autoptr(GPtrArray) addins = NULL;
+ GApplication *app;
+
+ g_return_if_fail (IDE_IS_WORKBENCH (self));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = ide_task_new (self, cancellable, callback, user_data);
+ ide_task_set_source_tag (task, ide_workbench_unload_async);
+
+ if (self->unloaded)
+ {
+ ide_task_return_boolean (task, TRUE);
+ return;
+ }
+
+ self->unloaded = TRUE;
+
+ /* Keep the GApplication alive for the lifetime of the task */
+ app = g_application_get_default ();
+ g_signal_connect_object (task,
+ "notify::completed",
+ G_CALLBACK (g_application_release),
+ app,
+ G_CONNECT_SWAPPED);
+ g_application_hold (app);
+
+ /*
+ * Remove our workbench from the application, so that no new
+ * open-file requests can keep us alive while we're shutting
+ * down.
+ */
+
+ ide_application_remove_workbench (IDE_APPLICATION (app), self);
+
+ /* If we haven't loaded a project, then there is nothing to
+ * do right now, just let ide_workbench_addin_unload() be called
+ * when the workbench disposes.
+ */
+ if (self->project_info == NULL)
+ {
+ ide_workbench_unload_project_completed (self, task);
+ return;
+ }
+
+ addins = ide_workbench_collect_addins (self);
+ ide_task_set_task_data (task, g_ptr_array_ref (addins), g_ptr_array_unref);
+
+ /* First unload the session while we are stable */
+ ide_session_save_async (self->session,
+ self,
+ cancellable,
+ ide_workbench_session_save_cb,
+ g_steal_pointer (&task));
+
+}
+
+/**
+ * ide_workbench_unload_finish:
+ * @self: an #IdeWorkbench
+ * @result: a #GAsyncResult provided to callback
+ * @error: a location for a #GError, or %NULL
+
+ * Completes a request to unload the workbench.
+ *
+ * Returns: %TRUE if the workbench was unloaded successfully,
+ * otherwise %FALSE and @error is set.
+ *
+ * Since: 3.32
+ */
+gboolean
+ide_workbench_unload_finish (IdeWorkbench *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (IDE_IS_WORKBENCH (self), FALSE);
+ g_return_val_if_fail (IDE_IS_TASK (result), FALSE);
+
+ return ide_task_propagate_boolean (IDE_TASK (result), error);
+}
+
+static void
+ide_workbench_open_all_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeWorkbench *self = (IdeWorkbench *)object;
+ g_autoptr(IdeTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+ gint *n_active;
+
+ g_assert (IDE_IS_WORKBENCH (self));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_TASK (task));
+
+ if (!ide_workbench_open_finish (self, result, &error))
+ g_message ("Failed to open file: %s", error->message);
+
+ n_active = ide_task_get_task_data (task);
+ g_assert (n_active != NULL);
+
+ (*n_active)--;
+
+ if (*n_active == 0)
+ ide_task_return_boolean (task, TRUE);
+}
+
+/**
+ * ide_workbench_open_all_async:
+ * @self: an #IdeWorkbench
+ * @files: (array length=n_files): an array of #GFile
+ * @n_files: number of #GFiles contained in @files
+ * @hint: (nullable): an optional hint about what addin to use
+ * @cancellable: (nullable): a #GCancellable
+ * @callback: a #GAsyncReadyCallback to execute upon completion
+ * @user_data: closure data for @callback
+ *
+ * Requests that the workbench open all of the #GFile denoted by @files.
+ *
+ * If @hint is provided, that will be used to determine what workbench
+ * addin to use when opening the file. The @hint name should match the
+ * module name of the plugin.
+ *
+ * Call ide_workbench_open_finish() from @callback to complete this
+ * operation.
+ *
+ * Since: 3.32
+ */
+void
+ide_workbench_open_all_async (IdeWorkbench *self,
+ GFile **files,
+ guint n_files,
+ const gchar *hint,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(IdeTask) task = NULL;
+ g_autoptr(GPtrArray) ar = NULL;
+ gint *n_active;
+
+ g_return_if_fail (IDE_IS_WORKBENCH (self));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = ide_task_new (self, cancellable, callback, user_data);
+ ide_task_set_source_tag (task, ide_workbench_open_all_async);
+
+ if (n_files == 0)
+ {
+ ide_task_return_boolean (task, TRUE);
+ return;
+ }
+
+ ar = g_ptr_array_new_full (n_files, g_object_unref);
+ for (guint i = 0; i < n_files; i++)
+ g_ptr_array_add (ar, g_object_ref (files[i]));
+
+ n_active = g_new0 (gint, 1);
+ *n_active = ar->len;
+ ide_task_set_task_data (task, n_active, g_free);
+
+ for (guint i = 0; i < ar->len; i++)
+ {
+ GFile *file = g_ptr_array_index (ar, i);
+
+ ide_workbench_open_async (self,
+ file,
+ hint,
+ IDE_BUFFER_OPEN_FLAGS_NONE,
+ cancellable,
+ ide_workbench_open_all_cb,
+ g_object_ref (task));
+ }
+}
+
+/**
+ * ide_workbench_open_async:
+ * @self: an #IdeWorkbench
+ * @file: a #GFile
+ * @hint: (nullable): an optional hint about what addin to use
+ * @flags: optional flags when opening the file
+ * @cancellable: (nullable): a #GCancellable
+ * @callback: a #GAsyncReadyCallback to execute upon completion
+ * @user_data: closure data for @callback
+ *
+ * Requests that the workbench open @file.
+ *
+ * If @hint is provided, that will be used to determine what workbench
+ * addin to use when opening the file. The @hint name should match the
+ * module name of the plugin.
+ *
+ * @flags may be ignored by some backends.
+ *
+ * Since: 3.32
+ */
+void
+ide_workbench_open_async (IdeWorkbench *self,
+ GFile *file,
+ const gchar *hint,
+ IdeBufferOpenFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (IDE_IS_WORKBENCH (self));
+ g_return_if_fail (G_IS_FILE (file));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ ide_workbench_open_at_async (self,
+ file,
+ hint,
+ -1,
+ -1,
+ flags,
+ cancellable,
+ callback,
+ user_data);
+}
+
+static void
+ide_workbench_open_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ IdeWorkbenchAddin *addin = (IdeWorkbenchAddin *)object;
+ IdeWorkbenchAddin *next;
+ g_autoptr(IdeTask) task = user_data;
+ g_autoptr(GError) error = NULL;
+ GCancellable *cancellable;
+ Open *o;
+
+ g_assert (IDE_IS_WORKBENCH_ADDIN (addin));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_TASK (task));
+
+ cancellable = ide_task_get_cancellable (task);
+ o = ide_task_get_task_data (task);
+
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+ g_assert (o != NULL);
+ g_assert (o->addins != NULL);
+ g_assert (o->addins->len > 0);
+
+ if (ide_workbench_addin_open_finish (addin, result, &error))
+ {
+ ide_task_return_boolean (task, TRUE);
+ return;
+ }
+
+ g_debug ("%s did not open the file, trying next.",
+ G_OBJECT_TYPE_NAME (addin));
+
+ g_ptr_array_remove (o->addins, addin);
+
+ /*
+ * We failed to open the file, try the next addin that is
+ * left which said it supported the content-type.
+ */
+
+ if (o->addins->len == 0)
+ {
+ ide_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "Failed to locate addin supporting file");
+ return;
+ }
+
+ next = g_ptr_array_index (o->addins, 0);
+
+ ide_workbench_addin_open_at_async (next,
+ o->file,
+ o->content_type,
+ o->at_line,
+ o->at_line_offset,
+ o->flags,
+ cancellable,
+ ide_workbench_open_cb,
+ g_steal_pointer (&task));
+}
+
+static gint
+sort_by_priority (gconstpointer a,
+ gconstpointer b,
+ gpointer user_data)
+{
+ IdeWorkbenchAddin *addin_a = *(IdeWorkbenchAddin **)a;
+ IdeWorkbenchAddin *addin_b = *(IdeWorkbenchAddin **)b;
+ Open *o = user_data;
+ gint prio_a = 0;
+ gint prio_b = 0;
+
+ if (!ide_workbench_addin_can_open (addin_a, o->file, o->content_type, &prio_a))
+ return 1;
+
+ if (!ide_workbench_addin_can_open (addin_b, o->file, o->content_type, &prio_b))
+ return -1;
+
+ return prio_a - prio_b;
+}
+
+static void
+ide_workbench_open_query_info_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GFile *file = (GFile *)object;
+ g_autoptr(IdeTask) task = user_data;
+ g_autoptr(GFileInfo) info = NULL;
+ g_autoptr(GError) error = NULL;
+ IdeWorkbenchAddin *first;
+ GCancellable *cancellable;
+ Open *o;
+
+ g_assert (G_IS_FILE (file));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (IDE_IS_TASK (task));
+
+ cancellable = ide_task_get_cancellable (task);
+ o = ide_task_get_task_data (task);
+
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+ g_assert (o != NULL);
+ g_assert (o->addins != NULL);
+ g_assert (o->addins->len > 0);
+
+ if ((info = g_file_query_info_finish (file, result, &error)))
+ o->content_type = g_strdup (g_file_info_get_content_type (info));
+
+ /* Remove unsupported addins while iterating backwards so that
+ * we can preserve the ordering of the array as we go.
+ */
+ for (guint i = o->addins->len; i > 0; i--)
+ {
+ IdeWorkbenchAddin *addin = g_ptr_array_index (o->addins, i - 1);
+ gint prio = G_MAXINT;
+
+ if (!ide_workbench_addin_can_open (addin, o->file, o->content_type, &prio))
+ {
+ g_ptr_array_remove_index_fast (o->addins, i - 1);
+ if (o->preferred == addin)
+ g_clear_object (&o->preferred);
+ }
+ }
+
+ if (o->addins->len == 0)
+ {
+ ide_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "No addins can open the file");
+ return;
+ }
+
+ /*
+ * Now sort the addins by priority, so that we can attempt to load them
+ * in the preferred ordering.
+ */
+ g_ptr_array_sort_with_data (o->addins, sort_by_priority, o);
+
+ /*
+ * Ensure that we place the preferred at the head of the array, so
+ * that it gets preference over default priorities.
+ */
+ if (o->preferred != NULL)
+ {
+ g_ptr_array_insert (o->addins, 0, g_object_ref (o->preferred));
+
+ for (guint i = 1; i < o->addins->len; i++)
+ {
+ if (g_ptr_array_index (o->addins, i) == (gpointer)o->preferred)
+ {
+ g_ptr_array_remove_index (o->addins, i);
+ break;
+ }
+ }
+ }
+
+ /* Now start requesting that addins attempt to load the file. */
+
+ first = g_ptr_array_index (o->addins, 0);
+
+ ide_workbench_addin_open_at_async (first,
+ o->file,
+ o->content_type,
+ o->at_line,
+ o->at_line_offset,
+ o->flags,
+ cancellable,
+ ide_workbench_open_cb,
+ g_steal_pointer (&task));
+}
+
+/**
+ * ide_workbench_open_at_async:
+ * @self: an #IdeWorkbench
+ * @file: a #GFile
+ * @hint: (nullable): an optional hint about what addin to use
+ * @at_line: the line number to open at, or -1 to ignore
+ * @at_line_offset: the line offset to open at, or -1 to ignore
+ * @flags: optional #IdeBufferOpenFlags
+ * @cancellable: (nullable): a #GCancellable
+ * @callback: a #GAsyncReadyCallback to execute upon completion
+ * @user_data: closure data for @callback
+ *
+ * Like ide_workbench_open_async(), this allows opening a file
+ * within the workbench. However, it also allows specifying a
+ * line and column offset within the file to focus. Usually, this
+ * only makes sense for files that can be opened in an editor.
+ *
+ * @at_line and @at_line_offset may be < 0 to ignore the parameters.
+ *
+ * @flags may be ignored by some backends
+ *
+ * Use ide_workbench_open_finish() to receive teh result of this
+ * asynchronous operation.
+ *
+ * Since: 3.32
+ */
+void
+ide_workbench_open_at_async (IdeWorkbench *self,
+ GFile *file,
+ const gchar *hint,
+ gint at_line,
+ gint at_line_offset,
+ IdeBufferOpenFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(IdeTask) task = NULL;
+ g_autoptr(GPtrArray) addins = NULL;
+ Open *o;
+
+ g_return_if_fail (IDE_IS_WORKBENCH (self));
+ g_return_if_fail (G_IS_FILE (file));
+ g_return_if_fail (self->unloaded == FALSE);
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ /* Canonicalize parameters */
+ if (at_line < 0)
+ at_line = -1;
+ if (at_line_offset < 0)
+ at_line_offset = -1;
+
+ task = ide_task_new (self, cancellable, callback, user_data);
+ ide_task_set_source_tag (task, ide_workbench_open_at_async);
+
+ /*
+ * Make sure we might have an addin to load after discovering
+ * the files content-type.
+ */
+ if (!(addins = ide_workbench_collect_addins (self)) || addins->len == 0)
+ {
+ ide_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ "No addins could open the file");
+ return;
+ }
+
+ o = g_slice_new0 (Open);
+ o->addins = g_ptr_array_ref (addins);
+ if (hint != NULL)
+ o->preferred = ide_workbench_find_addin (self, hint);
+ o->file = g_object_ref (file);
+ o->hint = g_strdup (hint);
+ o->flags = flags;
+ o->at_line = at_line;
+ o->at_line_offset = at_line_offset;
+ ide_task_set_task_data (task, o, open_free);
+
+ g_file_query_info_async (file,
+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+ G_FILE_QUERY_INFO_NONE,
+ G_PRIORITY_DEFAULT,
+ cancellable,
+ ide_workbench_open_query_info_cb,
+ g_steal_pointer (&task));
+}
+
+/**
+ * ide_workbench_open_finish:
+ * @self: an #IdeWorkbench
+ * @result: a #GAsyncResult provided to callback
+ * @error: a location for a #GError, or %NULL
+ *
+ * Completes a request to open a file using either
+ * ide_workbench_open_async() or ide_workbench_open_at_async().
+ *
+ * Returns: %TRUE if the file was successfully opened; otherwise
+ * %FALSE and @error is set.
+ *
+ * Since: 3.32
+ */
+gboolean
+ide_workbench_open_finish (IdeWorkbench *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (IDE_IS_WORKBENCH (self), FALSE);
+ g_return_val_if_fail (IDE_IS_TASK (result), FALSE);
+
+ return ide_task_propagate_boolean (IDE_TASK (result), error);
+}
+
+/**
+ * ide_workbench_get_current_workspace:
+ * @self: a #IdeWorkbench
+ *
+ * Gets the most recently focused workspace, which may be used to
+ * deliver events such as opening new pages.
+ *
+ * Returns: (transfer none) (nullable): an #IdeWorkspace or %NULL
+ *
+ * Since: 3.32
+ */
+IdeWorkspace *
+ide_workbench_get_current_workspace (IdeWorkbench *self)
+{
+ g_return_val_if_fail (IDE_IS_WORKBENCH (self), NULL);
+
+ if (self->mru_queue.length > 0)
+ return IDE_WORKSPACE (self->mru_queue.head->data);
+
+ return NULL;
+}
+
+/**
+ * ide_workbench_activate:
+ * @self: a #IdeWorkbench
+ *
+ * This function will attempt to raise the most recently focused workspace.
+ *
+ * Since: 3.32
+ */
+void
+ide_workbench_activate (IdeWorkbench *self)
+{
+ IdeWorkspace *workspace;
+
+ g_return_if_fail (IDE_IS_WORKBENCH (self));
+
+ if ((workspace = ide_workbench_get_current_workspace (self)))
+ ide_workbench_focus_workspace (self, workspace);
+}
+
+static void
+ide_workbench_propagate_vcs_cb (PeasExtensionSet *set,
+ PeasPluginInfo *plugin_info,
+ PeasExtension *exten,
+ gpointer user_data)
+{
+ IdeWorkbenchAddin *addin = (IdeWorkbenchAddin *)exten;
+ IdeVcs *vcs = user_data;
+
+ g_assert (PEAS_IS_EXTENSION_SET (set));
+ g_assert (plugin_info != NULL);
+ g_assert (IDE_IS_WORKBENCH_ADDIN (addin));
+ g_assert (!vcs || IDE_IS_VCS (vcs));
+
+ ide_workbench_addin_vcs_changed (addin, vcs);
+}
+
+/**
+ * ide_workbench_get_vcs:
+ * @self: a #IdeWorkbench
+ *
+ * Gets the #IdeVcs that has been loaded for the workbench, if any.
+ *
+ * Returns: (transfer none) (nullable): an #IdeVcs or %NULL
+ *
+ * Since: 3.32
+ */
+IdeVcs *
+ide_workbench_get_vcs (IdeWorkbench *self)
+{
+ g_return_val_if_fail (IDE_IS_WORKBENCH (self), NULL);
+
+ return self->vcs;
+}
+
+/**
+ * ide_workbench_get_vcs_monitor:
+ * @self: a #IdeWorkbench
+ *
+ * Gets the #IdeVcsMonitor for the workbench, if any.
+ *
+ * Returns: (transfer none) (nullable): an #IdeVcsMonitor or %NULL
+ *
+ * Since: 3.32
+ */
+IdeVcsMonitor *
+ide_workbench_get_vcs_monitor (IdeWorkbench *self)
+{
+ g_return_val_if_fail (IDE_IS_WORKBENCH (self), NULL);
+
+ return self->vcs_monitor;
+}
+
+static void
+remove_non_matching_vcs_cb (IdeObject *child,
+ IdeVcs *vcs)
+{
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_OBJECT (child));
+ g_assert (IDE_IS_VCS (vcs));
+
+ if (IDE_IS_VCS (child) && IDE_VCS (child) != vcs)
+ ide_object_destroy (child);
+}
+
+/**
+ * ide_workbench_set_vcs:
+ * @self: a #IdeWorkbench
+ * @vcs: (nullable): an #IdeVcs
+ *
+ * Sets the #IdeVcs for the workbench.
+ *
+ * Since: 3.32
+ */
+void
+ide_workbench_set_vcs (IdeWorkbench *self,
+ IdeVcs *vcs)
+{
+ g_autoptr(IdeVcs) local_vcs = NULL;
+ g_autoptr(GFile) local_workdir = NULL;
+ g_autoptr(GFile) workdir = NULL;
+
+ g_return_if_fail (IDE_IS_MAIN_THREAD ());
+ g_return_if_fail (IDE_IS_WORKBENCH (self));
+ g_return_if_fail (!vcs || IDE_IS_VCS (vcs));
+
+ if (vcs == self->vcs)
+ return;
+
+ if (vcs == NULL)
+ {
+ local_workdir = ide_context_ref_workdir (self->context);
+ vcs = local_vcs = IDE_VCS (ide_directory_vcs_new (local_workdir));
+ }
+
+ g_set_object (&self->vcs, vcs);
+ ide_object_append (IDE_OBJECT (self->context), IDE_OBJECT (vcs));
+ ide_object_foreach (IDE_OBJECT (self->context),
+ (GFunc)remove_non_matching_vcs_cb,
+ vcs);
+
+ if ((workdir = ide_vcs_get_workdir (vcs)))
+ ide_context_set_workdir (self->context, workdir);
+
+ ide_vcs_monitor_set_vcs (self->vcs_monitor, self->vcs);
+
+ peas_extension_set_foreach (self->addins,
+ ide_workbench_propagate_vcs_cb,
+ self->vcs);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_VCS]);
+}
+
+/**
+ * ide_workbench_get_build_system:
+ * @self: a #IdeWorkbench
+ *
+ * Gets the #IdeBuildSystem for the workbench, if any.
+ *
+ * Returns: (transfer none) (nullable): an #IdeBuildSystem or %NULL
+ *
+ * Since: 3.32
+ */
+IdeBuildSystem *
+ide_workbench_get_build_system (IdeWorkbench *self)
+{
+ g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
+ g_return_val_if_fail (IDE_IS_WORKBENCH (self), NULL);
+
+ return self->build_system;
+}
+
+static void
+remove_non_matching_build_systems_cb (IdeObject *child,
+ IdeBuildSystem *build_system)
+{
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_OBJECT (child));
+ g_assert (IDE_IS_BUILD_SYSTEM (build_system));
+
+ if (IDE_IS_BUILD_SYSTEM (child) && IDE_BUILD_SYSTEM (child) != build_system)
+ ide_object_destroy (child);
+}
+
+/**
+ * ide_workbench_set_build_system:
+ * @self: a #IdeWorkbench
+ * @build_system: (nullable): an #IdeBuildSystem or %NULL
+ *
+ * Sets the #IdeBuildSystem for the workbench.
+ *
+ * If @build_system is %NULL, then a fallback build system will be used
+ * instead. It does not provide building capabilities, but allows for some
+ * components that require a build system to continue functioning.
+ *
+ * Since: 3.32
+ */
+void
+ide_workbench_set_build_system (IdeWorkbench *self,
+ IdeBuildSystem *build_system)
+{
+ g_autoptr(IdeBuildSystem) local_build_system = NULL;
+ IdeBuildManager *build_manager;
+
+ g_return_if_fail (IDE_IS_WORKBENCH (self));
+ g_return_if_fail (!build_system || IDE_IS_BUILD_SYSTEM (build_system));
+
+ if (build_system == self->build_system)
+ return;
+
+ /* We want there to always be a build system available so that various
+ * plugins don't need lots of extra code to handle the %NULL case. So
+ * if @build_system is %NULL, then we'll create a fallback build system
+ * and assign that instead.
+ */
+
+ if (build_system == NULL)
+ build_system = local_build_system = ide_fallback_build_system_new ();
+
+ /* We want to add our new build system before removing the old build
+ * system to ensure there is always an #IdeBuildSystem child of the
+ * IdeContext.
+ */
+ g_set_object (&self->build_system, build_system);
+ ide_object_append (IDE_OBJECT (self->context), IDE_OBJECT (build_system));
+
+ /* Now remove any previous build-system from the context */
+ ide_object_foreach (IDE_OBJECT (self->context),
+ (GFunc)remove_non_matching_build_systems_cb,
+ build_system);
+
+ /* Ask the build-manager to setup a new pipeline */
+ if ((build_manager = ide_context_peek_child_typed (self->context, IDE_TYPE_BUILD_MANAGER)))
+ ide_build_manager_invalidate (build_manager);
+}
+
+/**
+ * ide_workbench_get_workspace_by_type:
+ * @self: a #IdeWorkbench
+ * @type: a #GType of a subclass of #IdeWorkspace
+ *
+ * Gets the most-recently-used workspace that matches @type.
+ *
+ * Returns: (transfer none) (nullable): an #IdeWorkspace or %NULL
+ *
+ * Since: 3.32
+ */
+IdeWorkspace *
+ide_workbench_get_workspace_by_type (IdeWorkbench *self,
+ GType type)
+{
+ g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
+ g_return_val_if_fail (IDE_IS_WORKBENCH (self), NULL);
+ g_return_val_if_fail (g_type_is_a (type, IDE_TYPE_WORKSPACE), NULL);
+
+ for (const GList *iter = self->mru_queue.head; iter; iter = iter->next)
+ {
+ if (G_TYPE_CHECK_INSTANCE_TYPE (iter->data, type))
+ return IDE_WORKSPACE (iter->data);
+ }
+
+ return NULL;
+}
+
+gboolean
+_ide_workbench_is_last_workspace (IdeWorkbench *self,
+ IdeWorkspace *workspace)
+{
+ g_return_val_if_fail (IDE_IS_MAIN_THREAD (), FALSE);
+ g_return_val_if_fail (IDE_IS_WORKBENCH (self), FALSE);
+
+ return self->mru_queue.length == 1 &&
+ g_queue_peek_head (&self->mru_queue) == (gpointer)workspace;
+}
+
+/**
+ * ide_workbench_has_project:
+ * @self: a #IdeWorkbench
+ *
+ * Returns %TRUE if a project is loaded (or currently loading) in the
+ * workbench.
+ *
+ * Returns: %TRUE if the workbench has a project
+ *
+ * Since: 3.32
+ */
+gboolean
+ide_workbench_has_project (IdeWorkbench *self)
+{
+ g_return_val_if_fail (IDE_IS_MAIN_THREAD (), FALSE);
+ g_return_val_if_fail (IDE_IS_WORKBENCH (self), FALSE);
+
+ return self->project_info != NULL;
+}
+
+/**
+ * ide_workbench_addin_find_by_module_name:
+ * @workbench: an #IdeWorkbench
+ * @module_name: the name of the addin module
+ *
+ * Finds the addin (if any) matching the plugin's @module_name.
+ *
+ * Returns: (transfer none) (nullable): an #IdeWorkbenchAddin or %NULL
+ *
+ * Since: 3.32
+ */
+IdeWorkbenchAddin *
+ide_workbench_addin_find_by_module_name (IdeWorkbench *workbench,
+ const gchar *module_name)
+{
+ PeasPluginInfo *plugin_info;
+ PeasExtension *ret = NULL;
+ PeasEngine *engine;
+
+ g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
+ g_return_val_if_fail (IDE_IS_WORKBENCH (workbench), NULL);
+ g_return_val_if_fail (module_name != NULL, NULL);
+
+ if (workbench->addins == NULL)
+ return NULL;
+
+ engine = peas_engine_get_default ();
+
+ if ((plugin_info = peas_engine_get_plugin_info (engine, module_name)))
+ ret = peas_extension_set_get_extension (workbench->addins, plugin_info);
+
+ return IDE_WORKBENCH_ADDIN (ret);
+}
diff --git a/src/libide/gui/ide-workbench.h b/src/libide/gui/ide-workbench.h
new file mode 100644
index 000000000..b3a0ae7cd
--- /dev/null
+++ b/src/libide/gui/ide-workbench.h
@@ -0,0 +1,144 @@
+/* ide-workbench.h
+ *
+ * Copyright 2014-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#if !defined (IDE_GUI_INSIDE) && !defined (IDE_GUI_COMPILATION)
+# error "Only <libide-gui.h> can be included directly."
+#endif
+
+#include <libide-core.h>
+#include <libide-foundry.h>
+#include <libide-projects.h>
+#include <libide-search.h>
+#include <libide-vcs.h>
+
+#include "ide-workspace.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_WORKBENCH (ide_workbench_get_type())
+
+IDE_AVAILABLE_IN_3_32
+G_DECLARE_FINAL_TYPE (IdeWorkbench, ide_workbench, IDE, WORKBENCH, GtkWindowGroup)
+
+IDE_AVAILABLE_IN_3_32
+IdeWorkbench *ide_workbench_new (void);
+IDE_AVAILABLE_IN_3_32
+IdeWorkbench *ide_workbench_new_for_context (IdeContext *context);
+IDE_AVAILABLE_IN_3_32
+void ide_workbench_activate (IdeWorkbench *self);
+IDE_AVAILABLE_IN_3_32
+IdeProjectInfo *ide_workbench_get_project_info (IdeWorkbench *self);
+IDE_AVAILABLE_IN_3_32
+gboolean ide_workbench_has_project (IdeWorkbench *self);
+IDE_AVAILABLE_IN_3_32
+IdeContext *ide_workbench_get_context (IdeWorkbench *self);
+IDE_AVAILABLE_IN_3_32
+IdeWorkspace *ide_workbench_get_current_workspace (IdeWorkbench *self);
+IDE_AVAILABLE_IN_3_32
+IdeWorkspace *ide_workbench_get_workspace_by_type (IdeWorkbench *self,
+ GType type);
+IDE_AVAILABLE_IN_3_32
+IdeSearchEngine *ide_workbench_get_search_engine (IdeWorkbench *self);
+IDE_AVAILABLE_IN_3_32
+IdeWorkbench *ide_workbench_from_widget (GtkWidget *widget);
+IDE_AVAILABLE_IN_3_32
+void ide_workbench_add_workspace (IdeWorkbench *self,
+ IdeWorkspace *workspace);
+IDE_AVAILABLE_IN_3_32
+void ide_workbench_remove_workspace (IdeWorkbench *self,
+ IdeWorkspace *workspace);
+IDE_AVAILABLE_IN_3_32
+void ide_workbench_focus_workspace (IdeWorkbench *self,
+ IdeWorkspace *workspace);
+IDE_AVAILABLE_IN_3_32
+void ide_workbench_foreach_workspace (IdeWorkbench *self,
+ GtkCallback callback,
+ gpointer user_data);
+IDE_AVAILABLE_IN_3_32
+void ide_workbench_foreach_page (IdeWorkbench *self,
+ GtkCallback callback,
+ gpointer user_data);
+IDE_AVAILABLE_IN_3_32
+void ide_workbench_load_project_async (IdeWorkbench *self,
+ IdeProjectInfo *project_info,
+ GType workspace_type,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+IDE_AVAILABLE_IN_3_32
+gboolean ide_workbench_load_project_finish (IdeWorkbench *self,
+ GAsyncResult *result,
+ GError **error);
+IDE_AVAILABLE_IN_3_32
+void ide_workbench_unload_async (IdeWorkbench *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+IDE_AVAILABLE_IN_3_32
+gboolean ide_workbench_unload_finish (IdeWorkbench *self,
+ GAsyncResult *result,
+ GError **error);
+IDE_AVAILABLE_IN_3_32
+void ide_workbench_open_async (IdeWorkbench *self,
+ GFile *file,
+ const gchar *hint,
+ IdeBufferOpenFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+IDE_AVAILABLE_IN_3_32
+void ide_workbench_open_at_async (IdeWorkbench *self,
+ GFile *file,
+ const gchar *hint,
+ gint at_line,
+ gint at_line_offset,
+ IdeBufferOpenFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+IDE_AVAILABLE_IN_3_32
+void ide_workbench_open_all_async (IdeWorkbench *self,
+ GFile **files,
+ guint n_files,
+ const gchar *hint,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+IDE_AVAILABLE_IN_3_32
+gboolean ide_workbench_open_finish (IdeWorkbench *self,
+ GAsyncResult *result,
+ GError **error);
+IDE_AVAILABLE_IN_3_32
+IdeVcs *ide_workbench_get_vcs (IdeWorkbench *self);
+IDE_AVAILABLE_IN_3_32
+void ide_workbench_set_vcs (IdeWorkbench *self,
+ IdeVcs *vcs);
+IDE_AVAILABLE_IN_3_32
+IdeVcsMonitor *ide_workbench_get_vcs_monitor (IdeWorkbench *self);
+IDE_AVAILABLE_IN_3_32
+IdeBuildSystem *ide_workbench_get_build_system (IdeWorkbench *self);
+IDE_AVAILABLE_IN_3_32
+void ide_workbench_set_build_system (IdeWorkbench *self,
+ IdeBuildSystem *build_system);
+
+
+G_END_DECLS
diff --git a/src/libide/workers/ide-worker-manager.c b/src/libide/gui/ide-worker-manager.c
similarity index 98%
rename from src/libide/workers/ide-worker-manager.c
rename to src/libide/gui/ide-worker-manager.c
index 85e6c4fb2..aef90a2dc 100644
--- a/src/libide/workers/ide-worker-manager.c
+++ b/src/libide/gui/ide-worker-manager.c
@@ -26,15 +26,13 @@
#include <gio/gio.h>
#include <gio/gunixsocketaddress.h>
#include <glib/gi18n.h>
+#include <libide-threading.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
-#include "ide-debug.h"
-
-#include "workers/ide-worker-process.h"
-#include "workers/ide-worker-manager.h"
-#include "threading/ide-task.h"
+#include "ide-worker-process.h"
+#include "ide-worker-manager.h"
struct _IdeWorkerManager
{
diff --git a/src/libide/workers/ide-worker-manager.h b/src/libide/gui/ide-worker-manager.h
similarity index 100%
rename from src/libide/workers/ide-worker-manager.h
rename to src/libide/gui/ide-worker-manager.h
diff --git a/src/libide/workers/ide-worker-process.c b/src/libide/gui/ide-worker-process.c
similarity index 98%
rename from src/libide/workers/ide-worker-process.c
rename to src/libide/gui/ide-worker-process.c
index 1a6111dcc..01f56efdf 100644
--- a/src/libide/workers/ide-worker-process.c
+++ b/src/libide/gui/ide-worker-process.c
@@ -24,13 +24,10 @@
#include <dazzle.h>
#include <libpeas/peas.h>
+#include <libide-threading.h>
-#include "ide-debug.h"
-
-#include "logging/ide-log.h"
-#include "workers/ide-worker-process.h"
-#include "workers/ide-worker.h"
-#include "threading/ide-task.h"
+#include "ide-worker-process.h"
+#include "ide-worker.h"
struct _IdeWorkerProcess
{
diff --git a/src/libide/workers/ide-worker-process.h b/src/libide/gui/ide-worker-process.h
similarity index 100%
rename from src/libide/workers/ide-worker-process.h
rename to src/libide/gui/ide-worker-process.h
diff --git a/src/libide/workers/ide-worker.c b/src/libide/gui/ide-worker.c
similarity index 97%
rename from src/libide/workers/ide-worker.c
rename to src/libide/gui/ide-worker.c
index 3fd51fa65..a01b64787 100644
--- a/src/libide/workers/ide-worker.c
+++ b/src/libide/gui/ide-worker.c
@@ -22,7 +22,9 @@
#include "config.h"
-#include "workers/ide-worker.h"
+#include <libide-core.h>
+
+#include "ide-worker.h"
G_DEFINE_INTERFACE (IdeWorker, ide_worker, G_TYPE_OBJECT)
diff --git a/src/libide/workers/ide-worker.h b/src/libide/gui/ide-worker.h
similarity index 96%
rename from src/libide/workers/ide-worker.h
rename to src/libide/gui/ide-worker.h
index be8002baf..0de20edd9 100644
--- a/src/libide/workers/ide-worker.h
+++ b/src/libide/gui/ide-worker.h
@@ -20,9 +20,7 @@
#pragma once
-#include <gio/gio.h>
-
-#include "ide-version-macros.h"
+#include <libide-core.h>
G_BEGIN_DECLS
diff --git a/src/libide/gui/ide-workspace-actions.c b/src/libide/gui/ide-workspace-actions.c
new file mode 100644
index 000000000..1257cd6ad
--- /dev/null
+++ b/src/libide/gui/ide-workspace-actions.c
@@ -0,0 +1,92 @@
+/* ide-workspace-actions.c
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-workspace-actions"
+
+#include "config.h"
+
+#include "ide-gui-private.h"
+
+static void
+ide_workspace_actions_close (GSimpleAction *action,
+ GVariant *param,
+ gpointer user_data)
+{
+ IdeWorkspace *self = user_data;
+
+ g_assert (G_IS_SIMPLE_ACTION (action));
+ g_assert (IDE_IS_WORKSPACE (self));
+
+ gtk_window_close (GTK_WINDOW (self));
+}
+
+static void
+ide_workspace_actions_show_menu (GSimpleAction *action,
+ GVariant *param,
+ gpointer user_data)
+{
+ IdeWorkspace *self = user_data;
+ GtkWidget *titlebar;
+
+ g_assert (G_IS_SIMPLE_ACTION (action));
+ g_assert (IDE_IS_WORKSPACE (self));
+
+ titlebar = gtk_window_get_titlebar (GTK_WINDOW (self));
+ if (GTK_IS_STACK (titlebar))
+ titlebar = gtk_stack_get_visible_child (GTK_STACK (titlebar));
+
+ if (IDE_IS_HEADER_BAR (titlebar))
+ _ide_header_bar_show_menu (IDE_HEADER_BAR (titlebar));
+}
+
+static void
+ide_workspace_actions_surface (GSimpleAction *action,
+ GVariant *param,
+ gpointer user_data)
+{
+ IdeWorkspace *self = user_data;
+ const gchar *surface;
+
+ g_assert (G_IS_SIMPLE_ACTION (action));
+ g_assert (param != NULL);
+ g_assert (g_variant_is_of_type (param, G_VARIANT_TYPE_STRING));
+ g_assert (IDE_IS_WORKSPACE (self));
+
+ surface = g_variant_get_string (param, NULL);
+
+ ide_workspace_set_visible_surface_name (self, surface);
+}
+
+static const GActionEntry actions[] = {
+ { "show-menu", ide_workspace_actions_show_menu },
+ { "surface", ide_workspace_actions_surface, "s" },
+ { "close", ide_workspace_actions_close },
+};
+
+void
+_ide_workspace_init_actions (IdeWorkspace *self)
+{
+ g_return_if_fail (IDE_IS_WORKSPACE (self));
+
+ g_action_map_add_action_entries (G_ACTION_MAP (self),
+ actions,
+ G_N_ELEMENTS (actions),
+ self);
+}
diff --git a/src/libide/gui/ide-workspace-addin.c b/src/libide/gui/ide-workspace-addin.c
new file mode 100644
index 000000000..c44fee4ca
--- /dev/null
+++ b/src/libide/gui/ide-workspace-addin.c
@@ -0,0 +1,118 @@
+/* ide-workspace-addin.c
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-workspace-addin"
+
+#include "config.h"
+
+#include "ide-workspace.h"
+#include "ide-workspace-addin.h"
+
+/**
+ * SECTION:ide-workspace-addin
+ * @title: IdeWorkspaceAddin
+ * @short_description: Extend the #IdeWorkspace windows
+ *
+ * The #IdeWorkspaceAddin is created with each #IdeWorkspace, allowing
+ * plugins a chance to modify each window that is created.
+ *
+ * If you set `X-Workspace-Kind=primary` in your `.plugin` file, your
+ * addin will only be loaded in the primary workspace. You may specify
+ * multiple workspace kinds such as `primary` or `secondary` separated
+ * by a comma such as `primary,secondary;`.
+ *
+ * Since: 3.32
+ */
+
+G_DEFINE_INTERFACE (IdeWorkspaceAddin, ide_workspace_addin, G_TYPE_OBJECT)
+
+static void
+ide_workspace_addin_default_init (IdeWorkspaceAddinInterface *iface)
+{
+}
+
+/**
+ * ide_workspace_addin_load:
+ * @self: a #IdeWorkspaceAddin
+ *
+ * Lods the #IdeWorkspaceAddin.
+ *
+ * This is a good place to modify the workspace from your addin.
+ * Remember to unmodify the workspace in ide_workspace_addin_unload().
+ *
+ * Since: 3.32
+ */
+void
+ide_workspace_addin_load (IdeWorkspaceAddin *self,
+ IdeWorkspace *workspace)
+{
+ g_return_if_fail (IDE_IS_MAIN_THREAD ());
+ g_return_if_fail (IDE_IS_WORKSPACE_ADDIN (self));
+ g_return_if_fail (IDE_IS_WORKSPACE (workspace));
+
+ if (IDE_WORKSPACE_ADDIN_GET_IFACE (self)->load)
+ IDE_WORKSPACE_ADDIN_GET_IFACE (self)->load (self, workspace);
+}
+
+/**
+ * ide_workspace_addin_unload:
+ * @self: a #IdeWorkspaceAddin
+ *
+ * Unloads the #IdeWorkspaceAddin.
+ *
+ * This is a good place to unmodify the workspace from anything you
+ * did in ide_workspace_addin_load().
+ *
+ * Since: 3.32
+ */
+void
+ide_workspace_addin_unload (IdeWorkspaceAddin *self,
+ IdeWorkspace *workspace)
+{
+ g_return_if_fail (IDE_IS_MAIN_THREAD ());
+ g_return_if_fail (IDE_IS_WORKSPACE_ADDIN (self));
+ g_return_if_fail (IDE_IS_WORKSPACE (workspace));
+
+ if (IDE_WORKSPACE_ADDIN_GET_IFACE (self)->unload)
+ IDE_WORKSPACE_ADDIN_GET_IFACE (self)->unload (self, workspace);
+}
+
+/**
+ * ide_workspace_addin_surface_set:
+ * @self: an #IdeWorkspaceAddin
+ * @surface: (nullable): an #IdeSurface or %NULL
+ *
+ * This function is called to notify the addin of the current surface.
+ * It may be set to %NULL before unloading the addin to allow addins
+ * to do surface change state handling and cleanup in one function.
+ *
+ * Since: 3.32
+ */
+void
+ide_workspace_addin_surface_set (IdeWorkspaceAddin *self,
+ IdeSurface *surface)
+{
+ g_return_if_fail (IDE_IS_MAIN_THREAD ());
+ g_return_if_fail (IDE_IS_WORKSPACE_ADDIN (self));
+ g_return_if_fail (!surface || IDE_IS_SURFACE (surface));
+
+ if (IDE_WORKSPACE_ADDIN_GET_IFACE (self)->surface_set)
+ IDE_WORKSPACE_ADDIN_GET_IFACE (self)->surface_set (self, surface);
+}
diff --git a/src/libide/gui/ide-workspace-addin.h b/src/libide/gui/ide-workspace-addin.h
new file mode 100644
index 000000000..8bf602c88
--- /dev/null
+++ b/src/libide/gui/ide-workspace-addin.h
@@ -0,0 +1,54 @@
+/* ide-workspace-addin.h
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include "ide-workspace.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_WORKSPACE_ADDIN (ide_workspace_addin_get_type())
+
+IDE_AVAILABLE_IN_3_32
+G_DECLARE_INTERFACE (IdeWorkspaceAddin, ide_workspace_addin, IDE, WORKSPACE_ADDIN, GObject)
+
+struct _IdeWorkspaceAddinInterface
+{
+ GTypeInterface parent_iface;
+
+ void (*load) (IdeWorkspaceAddin *self,
+ IdeWorkspace *workspace);
+ void (*unload) (IdeWorkspaceAddin *self,
+ IdeWorkspace *workspace);
+ void (*surface_set) (IdeWorkspaceAddin *self,
+ IdeSurface *surface);
+};
+
+IDE_AVAILABLE_IN_3_32
+void ide_workspace_addin_load (IdeWorkspaceAddin *self,
+ IdeWorkspace *workspace);
+IDE_AVAILABLE_IN_3_32
+void ide_workspace_addin_unload (IdeWorkspaceAddin *self,
+ IdeWorkspace *workspace);
+IDE_AVAILABLE_IN_3_32
+void ide_workspace_addin_surface_set (IdeWorkspaceAddin *self,
+ IdeSurface *surface);
+
+G_END_DECLS
diff --git a/src/libide/gui/ide-workspace.c b/src/libide/gui/ide-workspace.c
new file mode 100644
index 000000000..7db4e9a03
--- /dev/null
+++ b/src/libide/gui/ide-workspace.c
@@ -0,0 +1,971 @@
+/* ide-workspace.c
+ *
+ * Copyright 2014-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-workspace"
+
+#include "config.h"
+
+#include <libide-plugins.h>
+
+#include "ide-gui-global.h"
+#include "ide-gui-private.h"
+#include "ide-workspace.h"
+#include "ide-workspace-addin.h"
+
+#define MUX_ACTIONS_KEY "IDE_WORKSPACE_MUX_ACTIONS"
+
+typedef struct
+{
+ /* Used as a link in IdeWorkbench's GQueue to track the most-recently-used
+ * workspaces based on recent focus.
+ */
+ GList mru_link;
+
+ /* This cancellable auto-cancels when the window is destroyed using
+ * ::delete-event() so that async operations can be made to auto-cancel.
+ */
+ GCancellable *cancellable;
+
+ /* The context for our workbench. It may not have a project loaded until
+ * the ide_workbench_load_project_async() workflow has been called, but it
+ * is usable without a project (albeit restricted).
+ */
+ IdeContext *context;
+
+ /* Our addins for the workspace window, that are limited by the "kind" of
+ * workspace that is loaded. Plugin files can specify X-Workspace-Kind to
+ * limit the plugin to specific type(s) of workspace.
+ */
+ IdeExtensionSetAdapter *addins;
+
+ /* We use an overlay as our top-most child so that plugins can potentially
+ * render any widget a layer above the UI.
+ */
+ GtkOverlay *overlay;
+
+ /* All workspaces are comprised of a series of "surfaces". However there may
+ * only ever be a single surface in a workspace (such as the editor workspace
+ * which is dedicated for editing).
+ */
+ GtkStack *surfaces;
+
+ /* The event box ensures that we can have events that will be used by the
+ * fullscreen overlay so that it gets delivery of crossing events.
+ */
+ GtkEventBox *event_box;
+
+ /* A MRU that is updated as pages are focused. It allows us to move through
+ * the pages in the order they've been most-recently focused.
+ */
+ GQueue page_mru;
+} IdeWorkspacePrivate;
+
+typedef struct
+{
+ GtkCallback callback;
+ gpointer user_data;
+} ForeachPage;
+
+enum {
+ SURFACE_SET,
+ N_SIGNALS
+};
+
+enum {
+ PROP_0,
+ PROP_CONTEXT,
+ PROP_VISIBLE_SURFACE,
+ N_PROPS
+};
+
+static void buildable_iface_init (GtkBuildableIface *iface);
+
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (IdeWorkspace, ide_workspace, DZL_TYPE_APPLICATION_WINDOW,
+ G_ADD_PRIVATE (IdeWorkspace)
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, buildable_iface_init))
+
+static GParamSpec *properties [N_PROPS];
+static guint signals [N_SIGNALS];
+
+static void
+ide_workspace_addin_added_cb (IdeExtensionSetAdapter *set,
+ PeasPluginInfo *plugin_info,
+ PeasExtension *exten,
+ gpointer user_data)
+{
+ IdeWorkspaceAddin *addin = (IdeWorkspaceAddin *)exten;
+ IdeWorkspace *self = user_data;
+
+ g_assert (IDE_IS_EXTENSION_SET_ADAPTER (set));
+ g_assert (plugin_info != NULL);
+ g_assert (IDE_IS_WORKSPACE_ADDIN (addin));
+ g_assert (IDE_IS_WORKSPACE (self));
+
+ g_debug ("Loading workspace addin from module %s",
+ peas_plugin_info_get_module_name (plugin_info));
+
+ ide_workspace_addin_load (addin, self);
+}
+
+static void
+ide_workspace_addin_removed_cb (IdeExtensionSetAdapter *set,
+ PeasPluginInfo *plugin_info,
+ PeasExtension *exten,
+ gpointer user_data)
+{
+ IdeWorkspaceAddin *addin = (IdeWorkspaceAddin *)exten;
+ IdeWorkspace *self = user_data;
+
+ g_assert (IDE_IS_EXTENSION_SET_ADAPTER (set));
+ g_assert (plugin_info != NULL);
+ g_assert (IDE_IS_WORKSPACE_ADDIN (addin));
+ g_assert (IDE_IS_WORKSPACE (self));
+
+ g_debug ("Unloading workspace addin from module %s",
+ peas_plugin_info_get_module_name (plugin_info));
+
+ ide_workspace_addin_surface_set (addin, NULL);
+ ide_workspace_addin_unload (addin, self);
+}
+
+static void
+ide_workspace_real_context_set (IdeWorkspace *self,
+ IdeContext *context)
+{
+ IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_WORKSPACE (self));
+ g_assert (IDE_IS_CONTEXT (context));
+
+ priv->addins = ide_extension_set_adapter_new (NULL,
+ NULL,
+ IDE_TYPE_WORKSPACE_ADDIN,
+ "Workspace-Kind",
+ IDE_WORKSPACE_GET_CLASS (self)->kind);
+
+ g_signal_connect (priv->addins,
+ "extension-added",
+ G_CALLBACK (ide_workspace_addin_added_cb),
+ self);
+
+ g_signal_connect (priv->addins,
+ "extension-removed",
+ G_CALLBACK (ide_workspace_addin_removed_cb),
+ self);
+
+ ide_extension_set_adapter_foreach (priv->addins,
+ ide_workspace_addin_added_cb,
+ self);
+}
+
+static void
+ide_workspace_addin_surface_set_cb (IdeExtensionSetAdapter *set,
+ PeasPluginInfo *plugin_info,
+ PeasExtension *exten,
+ gpointer user_data)
+{
+ IdeWorkspaceAddin *addin = (IdeWorkspaceAddin *)exten;
+ IdeSurface *surface = user_data;
+
+ g_assert (IDE_IS_EXTENSION_SET_ADAPTER (set));
+ g_assert (plugin_info != NULL);
+ g_assert (IDE_IS_WORKSPACE_ADDIN (addin));
+ g_assert (!surface || IDE_IS_SURFACE (surface));
+
+ ide_workspace_addin_surface_set (addin, surface);
+}
+
+static void
+ide_workspace_real_surface_set (IdeWorkspace *self,
+ IdeSurface *surface)
+{
+ IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_WORKSPACE (self));
+ g_assert (!surface || IDE_IS_SURFACE (surface));
+
+ if (priv->addins != NULL)
+ ide_extension_set_adapter_foreach (priv->addins,
+ ide_workspace_addin_surface_set_cb,
+ surface);
+}
+
+/**
+ * ide_workspace_foreach_surface:
+ * @self: a #IdeWorkspace
+ * @callback: (scope call): a #GtkCallback to execute for every surface
+ * @user_data: user data for @callback
+ *
+ * Calls callback for every #IdeSurface based #GtkWidget that is registered
+ * in the workspace.
+ *
+ * Since: 3.32
+ */
+void
+ide_workspace_foreach_surface (IdeWorkspace *self,
+ GtkCallback callback,
+ gpointer user_data)
+{
+ IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
+
+ g_assert (IDE_IS_WORKSPACE (self));
+ g_assert (callback != NULL);
+
+ gtk_container_foreach (GTK_CONTAINER (priv->surfaces), callback, user_data);
+}
+
+static void
+ide_workspace_agree_to_shutdown_cb (GtkWidget *widget,
+ gpointer user_data)
+{
+ gboolean *blocked = user_data;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_SURFACE (widget));
+ g_assert (blocked != NULL);
+
+ *blocked |= !ide_surface_agree_to_shutdown (IDE_SURFACE (widget));
+}
+
+static gboolean
+ide_workspace_agree_to_shutdown (IdeWorkspace *self)
+{
+ gboolean blocked = FALSE;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (IDE_IS_WORKSPACE (self));
+
+ ide_workspace_foreach_surface (self,
+ ide_workspace_agree_to_shutdown_cb,
+ &blocked);
+
+ return !blocked;
+}
+
+static gboolean
+ide_workspace_delete_event (GtkWidget *widget,
+ GdkEventAny *any)
+{
+ IdeWorkspace *self = (IdeWorkspace *)widget;
+ IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
+ g_autoptr(IdeTask) task = NULL;
+ IdeWorkbench *workbench;
+
+ g_assert (IDE_IS_WORKSPACE (self));
+ g_assert (any != NULL);
+
+ /* TODO:
+ *
+ * If there are any active transfers, we want to ask the user if they
+ * are sure they want to exit and risk losing them. We can allow them
+ * to be completed in the background.
+ *
+ * Note that we only want to do this on the final workspace window.
+ */
+
+ if (!ide_workspace_agree_to_shutdown (self))
+ return GDK_EVENT_STOP;
+
+ g_cancellable_cancel (priv->cancellable);
+
+ workbench = ide_widget_get_workbench (widget);
+
+ if (ide_workbench_has_project (workbench) &&
+ _ide_workbench_is_last_workspace (workbench, self))
+ {
+ gtk_widget_hide (GTK_WIDGET (self));
+ ide_workbench_unload_async (workbench, NULL, NULL, NULL);
+ return GDK_EVENT_STOP;
+ }
+
+ return GDK_EVENT_PROPAGATE;
+}
+
+static void
+ide_workspace_notify_surface_cb (IdeWorkspace *self,
+ GParamSpec *pspec,
+ GtkStack *surfaces)
+{
+ GtkWidget *visible_child;
+ IdeHeaderBar *header_bar;
+
+ g_assert (IDE_IS_WORKSPACE (self));
+ g_assert (GTK_IS_STACK (surfaces));
+
+ visible_child = gtk_stack_get_visible_child (surfaces);
+ if (!IDE_IS_SURFACE (visible_child))
+ visible_child = NULL;
+
+ if (visible_child != NULL)
+ gtk_widget_grab_focus (visible_child);
+
+ if ((header_bar = ide_workspace_get_header_bar (self)))
+ {
+ if (visible_child != NULL)
+ dzl_gtk_widget_mux_action_groups (GTK_WIDGET (header_bar), visible_child, MUX_ACTIONS_KEY);
+ else
+ dzl_gtk_widget_mux_action_groups (GTK_WIDGET (header_bar), NULL, MUX_ACTIONS_KEY);
+ }
+
+ g_signal_emit (self, signals [SURFACE_SET], 0, visible_child);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_VISIBLE_SURFACE]);
+}
+
+static void
+ide_workspace_destroy (GtkWidget *widget)
+{
+ IdeWorkspace *self = (IdeWorkspace *)widget;
+ IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
+ GtkWindowGroup *group;
+
+ g_assert (IDE_IS_WORKSPACE (self));
+
+ ide_clear_and_destroy_object (&priv->addins);
+
+ group = gtk_window_get_group (GTK_WINDOW (self));
+ if (IDE_IS_WORKBENCH (group))
+ ide_workbench_remove_workspace (IDE_WORKBENCH (group), self);
+
+ GTK_WIDGET_CLASS (ide_workspace_parent_class)->destroy (widget);
+}
+
+/**
+ * ide_workspace_class_set_kind:
+ * @klass: a #IdeWorkspaceClass
+ *
+ * Sets the shorthand name for the kind of workspace. This is used to limit
+ * what #IdeWorkspaceAddin may load within the workspace.
+ *
+ * Since: 3.32
+ */
+void
+ide_workspace_class_set_kind (IdeWorkspaceClass *klass,
+ const gchar *kind)
+{
+ g_return_if_fail (IDE_IS_WORKSPACE_CLASS (klass));
+
+ klass->kind = g_intern_string (kind);
+}
+
+
+static void
+ide_workspace_foreach_page_cb (GtkWidget *widget,
+ gpointer user_data)
+{
+ ForeachPage *state = user_data;
+
+ if (IDE_IS_SURFACE (widget))
+ ide_surface_foreach_page (IDE_SURFACE (widget), state->callback, state->user_data);
+}
+
+static void
+ide_workspace_real_foreach_page (IdeWorkspace *self,
+ GtkCallback callback,
+ gpointer user_data)
+{
+ IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
+ ForeachPage state = { callback, user_data };
+
+ g_assert (IDE_IS_WORKSPACE (self));
+ g_assert (callback != NULL);
+
+ gtk_container_foreach (GTK_CONTAINER (priv->surfaces),
+ ide_workspace_foreach_page_cb,
+ &state);
+}
+
+static void
+ide_workspace_set_surface_fullscreen_cb (GtkWidget *widget,
+ gpointer user_data)
+{
+ g_assert (GTK_IS_WIDGET (widget));
+
+ if (IDE_IS_SURFACE (widget))
+ _ide_surface_set_fullscreen (IDE_SURFACE (widget), !!user_data);
+}
+
+static void
+ide_workspace_real_set_fullscreen (DzlApplicationWindow *window,
+ gboolean fullscreen)
+{
+ IdeWorkspace *self = (IdeWorkspace *)window;
+ IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
+
+ g_assert (IDE_IS_WORKSPACE (self));
+
+ DZL_APPLICATION_WINDOW_CLASS (ide_workspace_parent_class)->set_fullscreen (window, fullscreen);
+
+ gtk_container_foreach (GTK_CONTAINER (priv->surfaces),
+ ide_workspace_set_surface_fullscreen_cb,
+ GUINT_TO_POINTER (fullscreen));
+}
+
+static void
+ide_workspace_grab_focus (GtkWidget *widget)
+{
+ IdeWorkspace *self = (IdeWorkspace *)widget;
+ IdeSurface *surface;
+
+ g_assert (IDE_IS_WORKSPACE (self));
+
+ if ((surface = ide_workspace_get_visible_surface (self)))
+ gtk_widget_grab_focus (GTK_WIDGET (surface));
+}
+
+static void
+ide_workspace_finalize (GObject *object)
+{
+ IdeWorkspace *self = (IdeWorkspace *)object;
+ IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
+
+ g_clear_object (&priv->context);
+ g_clear_object (&priv->cancellable);
+
+ G_OBJECT_CLASS (ide_workspace_parent_class)->finalize (object);
+}
+
+static void
+ide_workspace_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdeWorkspace *self = IDE_WORKSPACE (object);
+
+ switch (prop_id)
+ {
+ case PROP_CONTEXT:
+ g_value_set_object (value, ide_workspace_get_context (self));
+ break;
+
+ case PROP_VISIBLE_SURFACE:
+ g_value_set_object (value, ide_workspace_get_visible_surface (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_workspace_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdeWorkspace *self = IDE_WORKSPACE (object);
+
+ switch (prop_id)
+ {
+ case PROP_VISIBLE_SURFACE:
+ ide_workspace_set_visible_surface (self, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_workspace_class_init (IdeWorkspaceClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ DzlApplicationWindowClass *window_class = DZL_APPLICATION_WINDOW_CLASS (klass);
+
+ object_class->finalize = ide_workspace_finalize;
+ object_class->get_property = ide_workspace_get_property;
+ object_class->set_property = ide_workspace_set_property;
+
+ widget_class->destroy = ide_workspace_destroy;
+ widget_class->delete_event = ide_workspace_delete_event;
+ widget_class->grab_focus = ide_workspace_grab_focus;
+
+ window_class->set_fullscreen = ide_workspace_real_set_fullscreen;
+
+ klass->foreach_page = ide_workspace_real_foreach_page;
+ klass->context_set = ide_workspace_real_context_set;
+ klass->surface_set = ide_workspace_real_surface_set;
+
+ /**
+ * IdeWorkspace:context:
+ *
+ * The "context" property is the #IdeContext for the workspace. This is set
+ * when the workspace joins a workbench.
+ *
+ * Since: 3.32
+ */
+ properties [PROP_CONTEXT] =
+ g_param_spec_object ("context",
+ "Context",
+ "The IdeContext for the workspace, inherited from workbench",
+ IDE_TYPE_CONTEXT,
+ (G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ /**
+ * IdeWorkspace:visible-surface:
+ *
+ * The "visible-surface" property contains the currently foremost surface
+ * in the workspaces stack of surfaces. Usually, this is the editor surface,
+ * but may be other surfaces such as build preferences, profiler, etc.
+ *
+ * Since: 3.32
+ */
+ properties [PROP_VISIBLE_SURFACE] =
+ g_param_spec_object ("visible-surface",
+ "Visible Surface",
+ "The currently visible surface",
+ IDE_TYPE_SURFACE,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+
+ /**
+ * IdeWorkspace::surface-set:
+ * @self: an #IdeWorkspace
+ * @surface: (nullable): an #IdeSurface
+ *
+ * The "surface-set" signal is emitted when the current surface changes
+ * within the workspace.
+ *
+ * Since: 3.32
+ */
+ signals [SURFACE_SET] =
+ g_signal_new ("surface-set",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (IdeWorkspaceClass, surface_set),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, IDE_TYPE_SURFACE);
+ g_signal_set_va_marshaller (signals [SURFACE_SET],
+ G_TYPE_FROM_CLASS (klass),
+ g_cclosure_marshal_VOID__OBJECTv);
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/libide-gui/ui/ide-workspace.ui");
+ gtk_widget_class_bind_template_child_private (widget_class, IdeWorkspace, event_box);
+ gtk_widget_class_bind_template_child_private (widget_class, IdeWorkspace, overlay);
+ gtk_widget_class_bind_template_child_private (widget_class, IdeWorkspace, surfaces);
+}
+
+static void
+ide_workspace_init (IdeWorkspace *self)
+{
+ IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
+ g_autofree gchar *app_id = NULL;
+
+ priv->mru_link.data = self;
+
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ g_signal_connect_object (priv->surfaces,
+ "notify::visible-child",
+ G_CALLBACK (ide_workspace_notify_surface_cb),
+ self,
+ G_CONNECT_SWAPPED);
+
+ /* Add org-gnome-Builder style CSS identifier */
+ app_id = g_strdelimit (g_strdup (ide_get_application_id ()), ".", '-');
+ dzl_gtk_widget_add_style_class (GTK_WIDGET (self), app_id);
+ dzl_gtk_widget_add_style_class (GTK_WIDGET (self), "workspace");
+
+ /* Add events for motion controller of fullscreen titlebar */
+ gtk_widget_add_events (GTK_WIDGET (priv->event_box),
+ (GDK_POINTER_MOTION_MASK |
+ GDK_ENTER_NOTIFY_MASK |
+ GDK_LEAVE_NOTIFY_MASK));
+
+ /* Initialize GActions for workspace */
+ _ide_workspace_init_actions (self);
+}
+
+GList *
+_ide_workspace_get_mru_link (IdeWorkspace *self)
+{
+ IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
+
+ g_assert (IDE_IS_WORKSPACE (self));
+
+ return &priv->mru_link;
+}
+
+/**
+ * ide_workspace_get_context:
+ *
+ * Gets the #IdeContext for the #IdeWorkspace, which is set when the
+ * workspace joins an #IdeWorkbench.
+ *
+ * Returns: (transfer none) (nullable): an #IdeContext or %NULL
+ *
+ * Since: 3.32
+ */
+IdeContext *
+ide_workspace_get_context (IdeWorkspace *self)
+{
+ IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_WORKSPACE (self), NULL);
+
+ return priv->context;
+}
+
+void
+_ide_workspace_set_context (IdeWorkspace *self,
+ IdeContext *context)
+{
+ IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_WORKSPACE (self));
+ g_return_if_fail (IDE_IS_CONTEXT (context));
+ g_return_if_fail (priv->context == NULL);
+
+ if (g_set_object (&priv->context, context))
+ {
+ if (IDE_WORKSPACE_GET_CLASS (self)->context_set)
+ IDE_WORKSPACE_GET_CLASS (self)->context_set (self, context);
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CONTEXT]);
+ }
+}
+
+/**
+ * ide_workspace_get_cancellable:
+ * @self: a #IdeWorkspace
+ *
+ * Gets a cancellable for a window. This is useful when you want operations
+ * to be cancelled if a window is closed.
+ *
+ * Returns: (transfer none): a #GCancellable
+ *
+ * Since: 3.32
+ */
+GCancellable *
+ide_workspace_get_cancellable (IdeWorkspace *self)
+{
+ IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
+ g_return_val_if_fail (IDE_IS_WORKSPACE (self), NULL);
+
+ if (priv->cancellable == NULL)
+ priv->cancellable = g_cancellable_new ();
+
+ return priv->cancellable;
+}
+
+/**
+ * ide_workspace_foreach_page:
+ * @self: a #IdeWorkspace
+ * @callback: (scope call): a callback to execute for each view
+ * @user_data: closure data for @callback
+ *
+ * Calls @callback for each #IdePage found within the workspace.
+ *
+ * Since: 3.32
+ */
+void
+ide_workspace_foreach_page (IdeWorkspace *self,
+ GtkCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (IDE_IS_WORKSPACE (self));
+ g_return_if_fail (callback != NULL);
+
+ if (IDE_WORKSPACE_GET_CLASS (self)->foreach_page)
+ IDE_WORKSPACE_GET_CLASS (self)->foreach_page (self, callback, user_data);
+}
+
+/**
+ * ide_workspace_get_header_bar:
+ * @self: a #IdeWorkspace
+ *
+ * Gets the headerbar for the workspace, if it is an #IdeHeaderBar.
+ * Also works around Gtk giving back a GtkStack for the header bar.
+ *
+ * Returns: (nullable) (transfer none): an #IdeHeaderBar or %NULL
+ *
+ * Since: 3.32
+ */
+IdeHeaderBar *
+ide_workspace_get_header_bar (IdeWorkspace *self)
+{
+ GtkWidget *titlebar;
+
+ g_return_val_if_fail (IDE_IS_WORKSPACE (self), NULL);
+
+ if ((titlebar = gtk_window_get_titlebar (GTK_WINDOW (self))))
+ {
+ if (GTK_IS_STACK (titlebar))
+ titlebar = gtk_stack_get_visible_child (GTK_STACK (titlebar));
+
+ if (IDE_IS_HEADER_BAR (titlebar))
+ return IDE_HEADER_BAR (titlebar);
+ }
+
+ return NULL;
+}
+
+/**
+ * ide_workspace_add_surface:
+ * @self: a #IdeWorkspace
+ *
+ * Adds a new #IdeSurface to the workspace.
+ *
+ * Since: 3.32
+ */
+void
+ide_workspace_add_surface (IdeWorkspace *self,
+ IdeSurface *surface)
+{
+ IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
+ g_autofree gchar *title = NULL;
+
+ g_return_if_fail (IDE_IS_WORKSPACE (self));
+ g_return_if_fail (IDE_IS_SURFACE (surface));
+
+ if (DZL_IS_DOCK_ITEM (surface))
+ title = dzl_dock_item_get_title (DZL_DOCK_ITEM (surface));
+
+ gtk_container_add_with_properties (GTK_CONTAINER (priv->surfaces), GTK_WIDGET (surface),
+ "name", gtk_widget_get_name (GTK_WIDGET (surface)),
+ "title", title,
+ NULL);
+}
+
+/**
+ * ide_workspace_set_visible_surface_name:
+ * @self: a #IdeWorkspace
+ * @visible_surface_name: the name of the #IdeSurface
+ *
+ * Sets the visible surface based on the name of the surface. The name of the
+ * surface comes from gtk_widget_get_name(), which should be set when creating
+ * the surface using gtk_widget_set_name().
+ *
+ * Since: 3.32
+ */
+void
+ide_workspace_set_visible_surface_name (IdeWorkspace *self,
+ const gchar *visible_surface_name)
+{
+ IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_WORKSPACE (self));
+ g_return_if_fail (visible_surface_name != NULL);
+
+ gtk_stack_set_visible_child_name (priv->surfaces, visible_surface_name);
+}
+
+/**
+ * ide_workspace_get_visible_surface:
+ * @self: a #IdeWorkspace
+ *
+ * Gets the currently visible #IdeSurface, or %NULL
+ *
+ * Returns: (transfer none) (nullable): an #IdeSurface or %NULL
+ *
+ * Since: 3.32
+ */
+IdeSurface *
+ide_workspace_get_visible_surface (IdeWorkspace *self)
+{
+ IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
+ GtkWidget *child;
+
+ g_return_val_if_fail (IDE_IS_WORKSPACE (self), NULL);
+
+ child = gtk_stack_get_visible_child (priv->surfaces);
+ if (!IDE_IS_SURFACE (child))
+ child = NULL;
+
+ return IDE_SURFACE (child);
+}
+
+/**
+ * ide_workspace_set_visible_surface:
+ * @self: a #IdeWorkspace
+ * @surface: an #IdeSurface
+ *
+ * Sets the #IdeWorkspace:visible-surface property which is the currently
+ * visible #IdeSurface in the workspace.
+ *
+ * Since: 3.32
+ */
+void
+ide_workspace_set_visible_surface (IdeWorkspace *self,
+ IdeSurface *surface)
+{
+ IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_WORKSPACE (self));
+ g_return_if_fail (IDE_IS_SURFACE (surface));
+
+ gtk_stack_set_visible_child (priv->surfaces, GTK_WIDGET (surface));
+}
+
+/**
+ * ide_workspace_get_surface_by_name:
+ * @self: a #IdeWorkspace
+ * @name: the name of the surface
+ *
+ * Locates an #IdeSurface that has been added to the workspace by the name
+ * that was registered for the widget using gtk_widget_set_name().
+ *
+ * Returns: (transfer none) (nullable): an #IdeSurface or %NULL
+ *
+ * Since: 3.32
+ */
+IdeSurface *
+ide_workspace_get_surface_by_name (IdeWorkspace *self,
+ const gchar *name)
+{
+ IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
+ GtkWidget *child;
+
+ g_return_val_if_fail (IDE_IS_WORKSPACE (self), NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ child = gtk_stack_get_child_by_name (priv->surfaces, name);
+
+ return IDE_IS_SURFACE (child) ? IDE_SURFACE (child) : NULL;
+}
+
+static GObject *
+ide_workspace_get_internal_child (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ const gchar *child_name)
+{
+ IdeWorkspace *self = (IdeWorkspace *)buildable;
+ IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
+
+ g_assert (GTK_IS_BUILDABLE (buildable));
+ g_assert (GTK_IS_BUILDER (builder));
+ g_assert (child_name != NULL);
+
+ if (ide_str_equal0 (child_name, "surfaces"))
+ return G_OBJECT (priv->surfaces);
+
+ return NULL;
+}
+
+static void
+buildable_iface_init (GtkBuildableIface *iface)
+{
+ iface->get_internal_child = ide_workspace_get_internal_child;
+}
+
+/**
+ * ide_workspace_get_overlay:
+ * @self: a #IdeWorkspace
+ *
+ * Gets a #GtkOverlay that contains all of the primary contents of the window
+ * (everything except the headerbar). This can be used by plugins to draw
+ * above the workspace contents.
+ *
+ * Returns: (transfer none): a #GtkOverlay
+ *
+ * Since: 3.32
+ */
+GtkOverlay *
+ide_workspace_get_overlay (IdeWorkspace *self)
+{
+ IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_WORKSPACE (self), NULL);
+
+ return priv->overlay;
+}
+
+/**
+ * ide_workspace_get_most_recent_page:
+ * @self: a #IdeWorkspace
+ *
+ * Gets the most recently focused #IdePage.
+ *
+ * Returns: (transfer none) (nullable): an #IdePage or %NULL
+ *
+ * Since: 3.32
+ */
+IdePage *
+ide_workspace_get_most_recent_page (IdeWorkspace *self)
+{
+ IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
+
+ g_return_val_if_fail (IDE_IS_WORKSPACE (self), NULL);
+
+ if (priv->page_mru.head != NULL)
+ return IDE_PAGE (priv->page_mru.head->data);
+
+ return NULL;
+}
+
+void
+_ide_workspace_add_page_mru (IdeWorkspace *self,
+ GList *mru_link)
+{
+ IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_WORKSPACE (self));
+ g_return_if_fail (mru_link != NULL);
+ g_return_if_fail (mru_link->prev == NULL);
+ g_return_if_fail (mru_link->next == NULL);
+ g_return_if_fail (IDE_IS_PAGE (mru_link->data));
+
+ g_debug ("Adding %s to page MRU",
+ G_OBJECT_TYPE_NAME (mru_link->data));
+
+ g_queue_push_head_link (&priv->page_mru, mru_link);
+}
+
+void
+_ide_workspace_remove_page_mru (IdeWorkspace *self,
+ GList *mru_link)
+{
+ IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_WORKSPACE (self));
+ g_return_if_fail (mru_link != NULL);
+ g_return_if_fail (IDE_IS_PAGE (mru_link->data));
+
+ g_debug ("Removing %s from page MRU",
+ G_OBJECT_TYPE_NAME (mru_link->data));
+
+ g_queue_unlink (&priv->page_mru, mru_link);
+}
+
+void
+_ide_workspace_move_front_page_mru (IdeWorkspace *self,
+ GList *mru_link)
+{
+ IdeWorkspacePrivate *priv = ide_workspace_get_instance_private (self);
+
+ g_return_if_fail (IDE_IS_WORKSPACE (self));
+ g_return_if_fail (mru_link != NULL);
+ g_return_if_fail (IDE_IS_PAGE (mru_link->data));
+
+ if (mru_link == priv->page_mru.head)
+ return;
+
+ g_debug ("Moving %s to front of page MRU",
+ G_OBJECT_TYPE_NAME (mru_link->data));
+
+ g_queue_unlink (&priv->page_mru, mru_link);
+ g_queue_push_head_link (&priv->page_mru, mru_link);
+}
diff --git a/src/libide/gui/ide-workspace.h b/src/libide/gui/ide-workspace.h
new file mode 100644
index 000000000..9e899c9d0
--- /dev/null
+++ b/src/libide/gui/ide-workspace.h
@@ -0,0 +1,96 @@
+/* ide-workspace.h
+ *
+ * Copyright 2014-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#if !defined (IDE_GUI_INSIDE) && !defined (IDE_GUI_COMPILATION)
+# error "Only <libide-gui.h> can be included directly."
+#endif
+
+#include <dazzle.h>
+#include <libide-core.h>
+#include <libide-projects.h>
+
+#include "ide-header-bar.h"
+#include "ide-page.h"
+#include "ide-surface.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_WORKSPACE (ide_workspace_get_type())
+
+IDE_AVAILABLE_IN_3_32
+G_DECLARE_DERIVABLE_TYPE (IdeWorkspace, ide_workspace, IDE, WORKSPACE, DzlApplicationWindow)
+
+struct _IdeWorkspaceClass
+{
+ DzlApplicationWindowClass parent_class;
+
+ const gchar *kind;
+
+ void (*context_set) (IdeWorkspace *self,
+ IdeContext *context);
+ void (*foreach_page) (IdeWorkspace *self,
+ GtkCallback callback,
+ gpointer user_data);
+ void (*surface_set) (IdeWorkspace *self,
+ IdeSurface *surface);
+
+ /*< private >*/
+ gpointer _reserved[32];
+};
+
+IDE_AVAILABLE_IN_3_32
+void ide_workspace_class_set_kind (IdeWorkspaceClass *klass,
+ const gchar *kind);
+IDE_AVAILABLE_IN_3_32
+IdeHeaderBar *ide_workspace_get_header_bar (IdeWorkspace *self);
+IDE_AVAILABLE_IN_3_32
+IdeContext *ide_workspace_get_context (IdeWorkspace *self);
+IDE_AVAILABLE_IN_3_32
+GCancellable *ide_workspace_get_cancellable (IdeWorkspace *self);
+IDE_AVAILABLE_IN_3_32
+void ide_workspace_foreach_page (IdeWorkspace *self,
+ GtkCallback callback,
+ gpointer user_data);
+IDE_AVAILABLE_IN_3_32
+void ide_workspace_foreach_surface (IdeWorkspace *self,
+ GtkCallback callback,
+ gpointer user_data);
+IDE_AVAILABLE_IN_3_32
+void ide_workspace_add_surface (IdeWorkspace *self,
+ IdeSurface *surface);
+IDE_AVAILABLE_IN_3_32
+IdeSurface *ide_workspace_get_surface_by_name (IdeWorkspace *self,
+ const gchar *name);
+IDE_AVAILABLE_IN_3_32
+void ide_workspace_set_visible_surface_name (IdeWorkspace *self,
+ const gchar *visible_surface_name);
+IDE_AVAILABLE_IN_3_32
+IdeSurface *ide_workspace_get_visible_surface (IdeWorkspace *self);
+IDE_AVAILABLE_IN_3_32
+void ide_workspace_set_visible_surface (IdeWorkspace *self,
+ IdeSurface *surface);
+IDE_AVAILABLE_IN_3_32
+GtkOverlay *ide_workspace_get_overlay (IdeWorkspace *self);
+IDE_AVAILABLE_IN_3_32
+IdePage *ide_workspace_get_most_recent_page (IdeWorkspace *self);
+
+G_END_DECLS
diff --git a/src/libide/gui/ide-workspace.ui b/src/libide/gui/ide-workspace.ui
new file mode 100644
index 000000000..6af729f22
--- /dev/null
+++ b/src/libide/gui/ide-workspace.ui
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="IdeWorkspace" parent="DzlApplicationWindow">
+ <child>
+ <object class="GtkEventBox" id="event_box">
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkOverlay" id="overlay">
+ <property name="visible">true</property>
+ <child>
+ <object class="GtkStack" id="surfaces">
+ <property name="homogeneous">false</property>
+ <property name="expand">true</property>
+ <property name="visible">true</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
+
diff --git a/src/libide/gui/libide-gui.gresource.xml b/src/libide/gui/libide-gui.gresource.xml
new file mode 100644
index 000000000..cec829c7c
--- /dev/null
+++ b/src/libide/gui/libide-gui.gresource.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/libide-gui">
+ <file preprocess="xml-stripblanks">gtk/menus.ui</file>
+ </gresource>
+ <gresource prefix="/org/gnome/libide-gui/ui">
+ <file preprocess="xml-stripblanks">ide-environment-editor-row.ui</file>
+ <file preprocess="xml-stripblanks">ide-frame-header.ui</file>
+ <file preprocess="xml-stripblanks">ide-frame.ui</file>
+ <file preprocess="xml-stripblanks">ide-header-bar.ui</file>
+ <file preprocess="xml-stripblanks">ide-notification-list-box-row.ui</file>
+ <file preprocess="xml-stripblanks">ide-notification-view.ui</file>
+ <file preprocess="xml-stripblanks">ide-notifications-button.ui</file>
+ <file preprocess="xml-stripblanks">ide-omni-bar.ui</file>
+ <file preprocess="xml-stripblanks">ide-panel.ui</file>
+ <file preprocess="xml-stripblanks">ide-preferences-language-row.ui</file>
+ <file preprocess="xml-stripblanks">ide-preferences-window.ui</file>
+ <file preprocess="xml-stripblanks">ide-primary-workspace.ui</file>
+ <file preprocess="xml-stripblanks">ide-run-button.ui</file>
+ <file preprocess="xml-stripblanks">ide-search-entry.ui</file>
+ <file preprocess="xml-stripblanks">ide-shortcuts-window.ui</file>
+ <file preprocess="xml-stripblanks">ide-workspace.ui</file>
+ </gresource>
+</gresources>
diff --git a/src/libide/gui/libide-gui.h b/src/libide/gui/libide-gui.h
new file mode 100644
index 000000000..258cd487e
--- /dev/null
+++ b/src/libide/gui/libide-gui.h
@@ -0,0 +1,70 @@
+/* ide-gui.h
+ *
+ * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+#include <libide-core.h>
+#include <libide-io.h>
+#include <libide-projects.h>
+#include <libide-threading.h>
+
+#define IDE_GUI_INSIDE
+
+#include "ide-application.h"
+#include "ide-application-addin.h"
+#include "ide-cell-renderer-fancy.h"
+#include "ide-command.h"
+#include "ide-command-provider.h"
+#include "ide-config-view-addin.h"
+#include "ide-environment-editor.h"
+#include "ide-frame.h"
+#include "ide-frame-addin.h"
+#include "ide-frame-header.h"
+#include "ide-header-bar.h"
+#include "ide-fancy-tree-view.h"
+#include "ide-grid.h"
+#include "ide-grid-column.h"
+#include "ide-gui-global.h"
+#include "ide-header-bar.h"
+#include "ide-marked-view.h"
+#include "ide-notifications-button.h"
+#include "ide-omni-bar-addin.h"
+#include "ide-omni-bar.h"
+#include "ide-page.h"
+#include "ide-pane.h"
+#include "ide-panel.h"
+#include "ide-preferences-addin.h"
+#include "ide-preferences-surface.h"
+#include "ide-preferences-window.h"
+#include "ide-primary-workspace.h"
+#include "ide-search-entry.h"
+#include "ide-session-addin.h"
+#include "ide-surface.h"
+#include "ide-surfaces-button.h"
+#include "ide-tagged-entry.h"
+#include "ide-transfer-button.h"
+#include "ide-transient-sidebar.h"
+#include "ide-workbench.h"
+#include "ide-workbench-addin.h"
+#include "ide-workspace.h"
+#include "ide-workspace-addin.h"
+
+#undef IDE_GUI_INSIDE
diff --git a/src/libide/gui/meson.build b/src/libide/gui/meson.build
new file mode 100644
index 000000000..3c494df9a
--- /dev/null
+++ b/src/libide/gui/meson.build
@@ -0,0 +1,212 @@
+libide_gui_header_subdir = join_paths(libide_header_subdir, 'gui')
+libide_include_directories += include_directories('.')
+
+libide_gui_generated_headers = []
+
+#
+# Public API Headers
+#
+
+libide_gui_public_headers = [
+ 'ide-application.h',
+ 'ide-application-addin.h',
+ 'ide-cell-renderer-fancy.h',
+ 'ide-command.h',
+ 'ide-command-provider.h',
+ 'ide-config-view-addin.h',
+ 'ide-environment-editor.h',
+ 'ide-fancy-tree-view.h',
+ 'ide-frame-addin.h',
+ 'ide-frame-header.h',
+ 'ide-frame.h',
+ 'ide-grid-column.h',
+ 'ide-grid.h',
+ 'ide-gui-global.h',
+ 'ide-header-bar.h',
+ 'ide-marked-view.h',
+ 'ide-notifications-button.h',
+ 'ide-omni-bar-addin.h',
+ 'ide-omni-bar.h',
+ 'ide-page.h',
+ 'ide-pane.h',
+ 'ide-panel.h',
+ 'ide-preferences-addin.h',
+ 'ide-preferences-surface.h',
+ 'ide-preferences-window.h',
+ 'ide-primary-workspace.h',
+ 'ide-search-entry.h',
+ 'ide-session-addin.h',
+ 'ide-surface.h',
+ 'ide-surfaces-button.h',
+ 'ide-tagged-entry.h',
+ 'ide-transfer-button.h',
+ 'ide-transient-sidebar.h',
+ 'ide-worker.h',
+ 'ide-workbench.h',
+ 'ide-workbench-addin.h',
+ 'ide-workspace.h',
+ 'ide-workspace-addin.h',
+ 'libide-gui.h',
+]
+
+install_headers(libide_gui_public_headers, subdir: libide_gui_header_subdir)
+
+#
+# Sources
+#
+
+libide_gui_private_headers = [
+ 'gs-markdown-private.h',
+ 'ide-application-private.h',
+ 'ide-environment-editor-row.h',
+ 'ide-frame-wrapper.h',
+ 'ide-gui-private.h',
+ 'ide-keybindings.h',
+ 'ide-notification-list-box-row-private.h',
+ 'ide-notifications-button-popover-private.h',
+ 'ide-notification-stack-private.h',
+ 'ide-notification-view-private.h',
+ 'ide-preferences-builtin-private.h',
+ 'ide-preferences-language-row-private.h',
+ 'ide-run-button.h',
+ 'ide-session-private.h',
+ 'ide-window-settings-private.h',
+ 'ide-shortcut-label-private.h',
+ 'ide-shortcuts-window-private.h',
+ 'ide-worker-manager.h',
+ 'ide-worker-process.h',
+]
+
+libide_gui_private_sources = [
+ 'gs-markdown.c',
+ 'ide-application-actions.c',
+ 'ide-application-color.c',
+ 'ide-application-shortcuts.c',
+ 'ide-application-plugins.c',
+ 'ide-environment-editor-row.c',
+ 'ide-frame-actions.c',
+ 'ide-frame-shortcuts.c',
+ 'ide-frame-wrapper.c',
+ 'ide-grid-actions.c',
+ 'ide-grid-column-actions.c',
+ 'ide-header-bar-shortcuts.c',
+ 'ide-keybindings.c',
+ 'ide-notification-list-box-row.c',
+ 'ide-notification-stack.c',
+ 'ide-notification-view.c',
+ 'ide-notifications-button-popover.c',
+ 'ide-preferences-builtin.c',
+ 'ide-preferences-language-row.c',
+ 'ide-primary-workspace-actions.c',
+ 'ide-run-button.c',
+ 'ide-session.c',
+ 'ide-shortcuts-window.c',
+ 'ide-window-settings.c',
+ 'ide-worker-manager.c',
+ 'ide-worker-process.c',
+ 'ide-workspace-actions.c',
+]
+
+libide_gui_public_sources = [
+ 'ide-application.c',
+ 'ide-application-addin.c',
+ 'ide-application-command-line.c',
+ 'ide-application-open.c',
+ 'ide-cell-renderer-fancy.c',
+ 'ide-command.c',
+ 'ide-command-provider.c',
+ 'ide-config-view-addin.c',
+ 'ide-environment-editor.c',
+ 'ide-fancy-tree-view.c',
+ 'ide-frame-addin.c',
+ 'ide-frame-header.c',
+ 'ide-frame.c',
+ 'ide-grid-column.c',
+ 'ide-grid.c',
+ 'ide-gui-global.c',
+ 'ide-header-bar.c',
+ 'ide-marked-view.c',
+ 'ide-notifications-button.c',
+ 'ide-omni-bar-addin.c',
+ 'ide-omni-bar.c',
+ 'ide-page.c',
+ 'ide-pane.c',
+ 'ide-panel.c',
+ 'ide-primary-workspace.c',
+ 'ide-preferences-addin.c',
+ 'ide-preferences-surface.c',
+ 'ide-preferences-window.c',
+ 'ide-search-entry.c',
+ 'ide-session-addin.c',
+ 'ide-shortcut-label.c',
+ 'ide-surface.c',
+ 'ide-surfaces-button.c',
+ 'ide-tagged-entry.c',
+ 'ide-transient-sidebar.c',
+ 'ide-transfer-button.c',
+ 'ide-workbench.c',
+ 'ide-workbench-addin.c',
+ 'ide-workspace.c',
+ 'ide-workspace-addin.c',
+ 'ide-worker.c',
+]
+
+libide_gui_sources = libide_gui_public_sources + libide_gui_private_sources
+
+#
+# Generated Resource Files
+#
+
+libide_gui_resources = gnome.compile_resources(
+ 'ide-gui-resources',
+ 'libide-gui.gresource.xml',
+ c_name: 'ide_gui',
+)
+libide_gui_generated_headers += [libide_gui_resources[1]]
+libide_gui_sources += libide_gui_resources[0]
+
+
+#
+# Dependencies
+#
+
+libide_gui_deps = [
+ libgio_dep,
+ libgtk_dep,
+ libgtksource_dep,
+ libdazzle_dep,
+ libpeas_dep,
+ libwebkit_dep,
+
+ libide_core_dep,
+ libide_io_dep,
+ libide_foundry_dep,
+ libide_debugger_dep,
+ libide_plugins_dep,
+ libide_projects_dep,
+ libide_search_dep,
+ libide_themes_dep,
+]
+
+#
+# Library Definitions
+#
+
+libide_gui = static_library('ide-gui-' + libide_api_version, libide_gui_sources,
+ dependencies: libide_gui_deps,
+ c_args: libide_args + release_args + ['-DIDE_GUI_COMPILATION'],
+)
+
+libide_gui_dep = declare_dependency(
+ sources: libide_gui_private_headers + libide_gui_generated_headers,
+ dependencies: libide_gui_deps,
+ link_whole: libide_gui,
+ include_directories: include_directories('.'),
+)
+
+gnome_builder_public_sources += files(libide_gui_public_sources)
+gnome_builder_public_headers += files(libide_gui_public_headers)
+gnome_builder_private_sources += files(libide_gui_private_sources)
+gnome_builder_private_headers += files(libide_gui_private_headers)
+gnome_builder_include_subdirs += libide_gui_header_subdir
+gnome_builder_gir_extra_args += ['--c-include=libide-gui.h', '-DIDE_GUI_COMPILATION']
diff --git a/src/libide/search/ide-search-engine.c b/src/libide/search/ide-search-engine.c
index 4320391ee..85e82d3f5 100644
--- a/src/libide/search/ide-search-engine.c
+++ b/src/libide/search/ide-search-engine.c
@@ -23,12 +23,12 @@
#include "config.h"
#include <libpeas/peas.h>
+#include <libide-core.h>
+#include <libide-threading.h>
-#include "search/ide-search-engine.h"
-#include "search/ide-search-provider.h"
-#include "search/ide-search-result.h"
-#include "threading/ide-task.h"
-#include "util/ide-glib.h"
+#include "ide-search-engine.h"
+#include "ide-search-provider.h"
+#include "ide-search-result.h"
#define DEFAULT_MAX_RESULTS 50
@@ -82,31 +82,65 @@ request_destroy (Request *r)
}
static void
-ide_search_engine_constructed (GObject *object)
+on_extension_added_cb (PeasExtensionSet *set,
+ PeasPluginInfo *plugin_info,
+ PeasExtension *exten,
+ gpointer user_data)
+{
+ ide_object_append (IDE_OBJECT (user_data), IDE_OBJECT (exten));
+}
+
+static void
+on_extension_removed_cb (PeasExtensionSet *set,
+ PeasPluginInfo *plugin_info,
+ PeasExtension *exten,
+ gpointer user_data)
+{
+ ide_object_remove (IDE_OBJECT (user_data), IDE_OBJECT (exten));
+}
+
+static void
+ide_search_engine_parent_set (IdeObject *object,
+ IdeObject *parent)
{
IdeSearchEngine *self = (IdeSearchEngine *)object;
- IdeContext *context;
g_assert (IDE_IS_SEARCH_ENGINE (self));
+ g_assert (!parent || IDE_IS_OBJECT (parent));
- G_OBJECT_CLASS (ide_search_engine_parent_class)->constructed (object);
-
- context = ide_object_get_context (IDE_OBJECT (self));
+ if (parent == NULL)
+ {
+ g_clear_object (&self->extensions);
+ return;
+ }
self->extensions = peas_extension_set_new (peas_engine_get_default (),
IDE_TYPE_SEARCH_PROVIDER,
- "context", context,
NULL);
+
+ g_signal_connect (self->extensions,
+ "extension-added",
+ G_CALLBACK (on_extension_added_cb),
+ self);
+
+ g_signal_connect (self->extensions,
+ "extension-removed",
+ G_CALLBACK (on_extension_removed_cb),
+ self);
+
+ peas_extension_set_foreach (self->extensions,
+ on_extension_added_cb,
+ self);
}
static void
-ide_search_engine_dispose (GObject *object)
+ide_search_engine_destroy (IdeObject *object)
{
IdeSearchEngine *self = (IdeSearchEngine *)object;
g_clear_object (&self->extensions);
- G_OBJECT_CLASS (ide_search_engine_parent_class)->dispose (object);
+ IDE_OBJECT_CLASS (ide_search_engine_parent_class)->destroy (object);
}
static void
@@ -131,11 +165,13 @@ ide_search_engine_get_property (GObject *object,
static void
ide_search_engine_class_init (IdeSearchEngineClass *klass)
{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GObjectClass *g_object_class = G_OBJECT_CLASS (klass);
+ IdeObjectClass *object_class = IDE_OBJECT_CLASS (klass);
+
+ g_object_class->get_property = ide_search_engine_get_property;
- object_class->constructed = ide_search_engine_constructed;
- object_class->dispose = ide_search_engine_dispose;
- object_class->get_property = ide_search_engine_get_property;
+ object_class->destroy = ide_search_engine_destroy;
+ object_class->parent_set = ide_search_engine_parent_set;
properties [PROP_BUSY] =
g_param_spec_boolean ("busy",
@@ -144,7 +180,7 @@ ide_search_engine_class_init (IdeSearchEngineClass *klass)
FALSE,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
- g_object_class_install_properties (object_class, N_PROPS, properties);
+ g_object_class_install_properties (g_object_class, N_PROPS, properties);
}
static void
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]