Commit fb47eb9a authored by Christian Hergert's avatar Christian Hergert
Browse files

libide-greeter: add new libide-greeter static library

This creates a new static library that contains the greeter. It breaks
the greeter into a separate window so that we can vastly simplify the
code in Builder with regards to workspaces.

Some code is moved to a plugin so that it can hook into command line
options and provide the new -g option to jump to the greeter.
parent 8d41d9a5
/* ide-genesis-addin.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-genesis-addin"
#include "config.h"
#include "genesis/ide-genesis-addin.h"
G_DEFINE_INTERFACE (IdeGenesisAddin, ide_genesis_addin, G_TYPE_OBJECT)
static void
ide_genesis_addin_default_init (IdeGenesisAddinInterface *iface)
{
g_object_interface_install_property (iface,
g_param_spec_boolean ("is-ready",
"Is Ready",
"If the project genesis can be executed",
FALSE,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
}
gchar *
ide_genesis_addin_get_title (IdeGenesisAddin *self)
{
g_return_val_if_fail (IDE_IS_GENESIS_ADDIN (self), NULL);
return IDE_GENESIS_ADDIN_GET_IFACE (self)->get_title (self);
}
gchar *
ide_genesis_addin_get_icon_name (IdeGenesisAddin *self)
{
g_return_val_if_fail (IDE_IS_GENESIS_ADDIN (self), NULL);
return IDE_GENESIS_ADDIN_GET_IFACE (self)->get_icon_name (self);
}
/**
* ide_genesis_addin_get_widget:
*
* Returns: (transfer none): a #GtkWidget.
*
* Since: 3.32
*/
GtkWidget *
ide_genesis_addin_get_widget (IdeGenesisAddin *self)
{
g_return_val_if_fail (IDE_IS_GENESIS_ADDIN (self), NULL);
return IDE_GENESIS_ADDIN_GET_IFACE (self)->get_widget (self);
}
void
ide_genesis_addin_run_async (IdeGenesisAddin *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IDE_IS_GENESIS_ADDIN (self));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
IDE_GENESIS_ADDIN_GET_IFACE (self)->run_async (self, cancellable, callback, user_data);
}
gboolean
ide_genesis_addin_run_finish (IdeGenesisAddin *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (IDE_IS_GENESIS_ADDIN (self), FALSE);
return IDE_GENESIS_ADDIN_GET_IFACE (self)->run_finish (self, result, error);
}
gint
ide_genesis_addin_get_priority (IdeGenesisAddin *self)
{
g_return_val_if_fail (IDE_IS_GENESIS_ADDIN (self), 0);
if (IDE_GENESIS_ADDIN_GET_IFACE (self)->get_priority)
return IDE_GENESIS_ADDIN_GET_IFACE (self)->get_priority (self);
return 0;
}
gchar *
ide_genesis_addin_get_label (IdeGenesisAddin *self)
{
g_return_val_if_fail (IDE_IS_GENESIS_ADDIN (self), NULL);
if (IDE_GENESIS_ADDIN_GET_IFACE (self)->get_label)
return IDE_GENESIS_ADDIN_GET_IFACE (self)->get_label (self);
return NULL;
}
gchar *
ide_genesis_addin_get_next_label (IdeGenesisAddin *self)
{
g_return_val_if_fail (IDE_IS_GENESIS_ADDIN (self), NULL);
if (IDE_GENESIS_ADDIN_GET_IFACE (self)->get_next_label)
return IDE_GENESIS_ADDIN_GET_IFACE (self)->get_next_label (self);
return NULL;
}
/**
* ide_genesis_addin_apply_uri:
* @self: an #IdeGenesisAddin
* @uri: an #IdeVcsUri
*
* If the #IdeGenesisAddin knows how to handle @uri, it should update it's
* UI to reflect the uri and return %TRUE. If so, ide_genesis_addin_run_async()
* will be called afterwards to begin a clone.
*
* Returns: %TRUE if @uri was handled; otherwise %FALSE.
*
* Since: 3.32
*/
gboolean
ide_genesis_addin_apply_uri (IdeGenesisAddin *self,
IdeVcsUri *uri)
{
g_return_val_if_fail (IDE_IS_GENESIS_ADDIN (self), FALSE);
g_return_val_if_fail (uri != NULL, FALSE);
if (IDE_GENESIS_ADDIN_GET_IFACE (self)->apply_uri)
return IDE_GENESIS_ADDIN_GET_IFACE (self)->apply_uri (self, uri);
return FALSE;
}
/* ide-genesis-addin.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>
#include "ide-version-macros.h"
#include "vcs/ide-vcs-uri.h"
G_BEGIN_DECLS
#define IDE_TYPE_GENESIS_ADDIN (ide_genesis_addin_get_type())
IDE_AVAILABLE_IN_3_32
G_DECLARE_INTERFACE (IdeGenesisAddin, ide_genesis_addin, IDE, GENESIS_ADDIN, GObject)
struct _IdeGenesisAddinInterface
{
GTypeInterface parent_interface;
gchar *(*get_title) (IdeGenesisAddin *self);
gchar *(*get_icon_name) (IdeGenesisAddin *self);
GtkWidget *(*get_widget) (IdeGenesisAddin *self);
void (*run_async) (IdeGenesisAddin *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean (*run_finish) (IdeGenesisAddin *self,
GAsyncResult *result,
GError **error);
gchar *(*get_label) (IdeGenesisAddin *self);
gchar *(*get_next_label) (IdeGenesisAddin *self);
gint (*get_priority) (IdeGenesisAddin *self);
gboolean (*apply_uri) (IdeGenesisAddin *self,
IdeVcsUri *uri);
};
IDE_AVAILABLE_IN_3_32
gboolean ide_genesis_addin_apply_uri (IdeGenesisAddin *self,
IdeVcsUri *uri);
IDE_AVAILABLE_IN_3_32
gchar *ide_genesis_addin_get_label (IdeGenesisAddin *self);
IDE_AVAILABLE_IN_3_32
gchar *ide_genesis_addin_get_next_label (IdeGenesisAddin *self);
IDE_AVAILABLE_IN_3_32
gint ide_genesis_addin_get_priority (IdeGenesisAddin *self);
IDE_AVAILABLE_IN_3_32
gchar *ide_genesis_addin_get_title (IdeGenesisAddin *self);
IDE_AVAILABLE_IN_3_32
gchar *ide_genesis_addin_get_icon_name (IdeGenesisAddin *self);
IDE_AVAILABLE_IN_3_32
GtkWidget *ide_genesis_addin_get_widget (IdeGenesisAddin *self);
IDE_AVAILABLE_IN_3_32
void ide_genesis_addin_run_async (IdeGenesisAddin *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
IDE_AVAILABLE_IN_3_32
gboolean ide_genesis_addin_run_finish (IdeGenesisAddin *self,
GAsyncResult *result,
GError **error);
G_END_DECLS
genesis_headers = [
'ide-genesis-addin.h',
]
genesis_sources = [
'ide-genesis-addin.c',
]
libide_public_headers += files(genesis_headers)
libide_public_sources += files(genesis_sources)
install_headers(genesis_headers, subdir: join_paths(libide_header_subdir, 'genesis'))
/* ide-clone-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-clone-surface"
#include "config.h"
#include <glib/gi18n.h>
#include <libpeas/peas.h>
#include <libide-vcs.h>
#include "ide-clone-surface.h"
#include "ide-greeter-private.h"
#include "ide-greeter-workspace.h"
struct _IdeCloneSurface
{
IdeSurface parent_instance;
/* This extension set contains IdeVcsCloner implementations which we
* use to validate URIs, as well as provide some toggles for how the
* user wants to perform the clone operation. Currently, we have a
* very limited set of cloning (basically just git), but that could be
* expanded in the future based on demand.
*/
PeasExtensionSet *addins;
guint n_addins;
/* We calculate the file to the target folder based on the vcs uri and
* the destination file chooser. It's cached here so that we don't have
* to recaclulate it in multiple code paths.
*/
GFile *destination;
/* Template Widgets */
DzlFileChooserEntry *destination_chooser;
GtkLabel *destination_label;
DzlRadioBox *kind_radio;
GtkLabel *kind_label;
GtkLabel *status_message;
GtkEntry *uri_entry;
GtkEntry *author_entry;
GtkEntry *email_entry;
GtkEntry *branch_entry;
GtkButton *clone_button;
GtkButton *cancel_button;
GtkStack *button_stack;
};
G_DEFINE_TYPE (IdeCloneSurface, ide_clone_surface, IDE_TYPE_SURFACE)
enum {
PROP_0,
PROP_URI,
N_PROPS
};
static GParamSpec *properties [N_PROPS];
/**
* ide_clone_surface_new:
*
* Create a new #IdeCloneSurface.
*
* Returns: (transfer full): a newly created #IdeCloneSurface
*
* Since: 3.32
*/
IdeCloneSurface *
ide_clone_surface_new (void)
{
return g_object_new (IDE_TYPE_CLONE_SURFACE, NULL);
}
static void
ide_clone_surface_addin_added_cb (PeasExtensionSet *set,
PeasPluginInfo *plugin_info,
PeasExtension *exten,
gpointer user_data)
{
IdeVcsCloner *cloner = (IdeVcsCloner *)exten;
IdeCloneSurface *self = user_data;
g_autofree gchar *title = NULL;
g_assert (IDE_IS_MAIN_THREAD ());
g_assert (PEAS_IS_EXTENSION_SET (set));
g_assert (plugin_info != NULL);
g_assert (IDE_IS_VCS_CLONER (cloner));
g_assert (IDE_IS_CLONE_SURFACE (self));
self->n_addins++;
title = ide_vcs_cloner_get_title (cloner);
dzl_radio_box_add_item (self->kind_radio,
peas_plugin_info_get_module_name (plugin_info),
title);
if (self->n_addins > 1)
{
gtk_widget_show (GTK_WIDGET (self->kind_label));
gtk_widget_show (GTK_WIDGET (self->kind_radio));
}
}
static void
ide_clone_surface_addin_removed_cb (PeasExtensionSet *set,
PeasPluginInfo *plugin_info,
PeasExtension *exten,
gpointer user_data)
{
IdeVcsCloner *cloner = (IdeVcsCloner *)exten;
IdeCloneSurface *self = user_data;
g_assert (IDE_IS_MAIN_THREAD ());
g_assert (PEAS_IS_EXTENSION_SET (set));
g_assert (plugin_info != NULL);
g_assert (IDE_IS_VCS_CLONER (cloner));
g_assert (IDE_IS_CLONE_SURFACE (self));
self->n_addins--;
dzl_radio_box_remove_item (self->kind_radio,
peas_plugin_info_get_module_name (plugin_info));
if (self->n_addins < 2)
{
gtk_widget_hide (GTK_WIDGET (self->kind_label));
gtk_widget_hide (GTK_WIDGET (self->kind_radio));
}
}
static void
ide_clone_surface_validate_cb (PeasExtensionSet *set,
PeasPluginInfo *plugin_info,
PeasExtension *exten,
gpointer user_data)
{
IdeVcsCloner *cloner = (IdeVcsCloner *)exten;
struct {
const gchar *text;
gchar *errmsg;
gboolean valid;
} *validate = user_data;
g_autofree gchar *errmsg = NULL;
g_assert (IDE_IS_VCS_CLONER (cloner));
if (validate->valid)
return;
validate->valid = ide_vcs_cloner_validate_uri (cloner, validate->text, &errmsg);
if (!validate->errmsg)
validate->errmsg = g_steal_pointer (&errmsg);
}
static void
ide_clone_surface_validate (IdeCloneSurface *self)
{
struct {
const gchar *text;
gchar *errmsg;
gboolean valid;
} validate;
g_assert (IDE_IS_CLONE_SURFACE (self));
validate.text = gtk_entry_get_text (self->uri_entry);
validate.errmsg = NULL;
validate.valid = FALSE;
if (self->addins != NULL)
peas_extension_set_foreach (self->addins,
ide_clone_surface_validate_cb,
&validate);
if (validate.valid)
dzl_gtk_widget_remove_style_class (GTK_WIDGET (self->uri_entry), "error");
else
dzl_gtk_widget_add_style_class (GTK_WIDGET (self->uri_entry), "error");
if (validate.errmsg)
gtk_widget_set_tooltip_text (GTK_WIDGET (self->uri_entry), validate.errmsg);
else
gtk_widget_set_tooltip_text (GTK_WIDGET (self->uri_entry), NULL);
g_free (validate.errmsg);
}
static void
ide_clone_surface_update (IdeCloneSurface *self)
{
g_autoptr(GFile) file = NULL;
g_autoptr(GFile) child_file = NULL;
g_autoptr(IdeVcsUri) uri = NULL;
g_autofree gchar *child = NULL;
g_autofree gchar *collapsed = NULL;
g_autofree gchar *formatted = NULL;
const gchar *text;
GtkEntry *entry;
g_assert (IDE_IS_CLONE_SURFACE (self));
ide_clone_surface_validate (self);
file = dzl_file_chooser_entry_get_file (self->destination_chooser);
text = gtk_entry_get_text (self->uri_entry);
uri = ide_vcs_uri_new (text);
if (uri != NULL)
child = ide_vcs_uri_get_clone_name (uri);
if (child)
child_file = g_file_get_child (file, child);
else
child_file = g_object_ref (file);
g_set_object (&self->destination, child_file);
collapsed = ide_path_collapse (g_file_peek_path (child_file));
entry = dzl_file_chooser_entry_get_entry (self->destination_chooser);
if (g_file_query_exists (child_file, NULL))
{
/* translators: %s is replaced with the path to the project */
formatted = g_strdup_printf (_("The directory “%s” already exists. Please choose another directory."),
collapsed);
dzl_gtk_widget_add_style_class (GTK_WIDGET (entry), "error");
}
else
{
/* translators: %s is replaced with the path to the project */
formatted = g_strdup_printf (_("Your project will be created at %s"), collapsed);
dzl_gtk_widget_remove_style_class (GTK_WIDGET (entry), "error");
}
gtk_label_set_label (self->destination_label, formatted);
}
static void
ide_clone_surface_uri_entry_changed (IdeCloneSurface *self,
GtkEntry *entry)
{
g_assert (IDE_IS_CLONE_SURFACE (self));
g_assert (GTK_IS_ENTRY (entry));
ide_clone_surface_update (self);
}
static void
ide_clone_surface_destination_changed (IdeCloneSurface *self,
GParamSpec *pspec,
DzlFileChooserEntry *chooser)
{
g_assert (IDE_IS_CLONE_SURFACE (self));
g_assert (DZL_IS_FILE_CHOOSER_ENTRY (chooser));
ide_clone_surface_update (self);
}
static void
ide_clone_surface_grab_focus (GtkWidget *widget)
{
gtk_widget_grab_focus (GTK_WIDGET (IDE_CLONE_SURFACE (widget)->uri_entry));
}
static void
ide_clone_surface_destroy (GtkWidget *widget)
{
IdeCloneSurface *self = (IdeCloneSurface *)widget;
g_assert (IDE_IS_MAIN_THREAD ());
g_assert (IDE_IS_CLONE_SURFACE (self));
g_clear_object (&self->addins);
g_clear_object (&self->destination);
GTK_WIDGET_CLASS (ide_clone_surface_parent_class)->destroy (widget);
}
static void
ide_clone_surface_constructed (GObject *object)
{
IdeCloneSurface *self = (IdeCloneSurface *)object;
g_autoptr(GFile) file = NULL;
G_OBJECT_CLASS (ide_clone_surface_parent_class)->constructed (object);
gtk_entry_set_text (self->author_entry, g_get_real_name ());
file = g_file_new_for_path (ide_get_projects_dir ());
dzl_file_chooser_entry_set_file (self->destination_chooser, file);
self->addins = peas_extension_set_new (peas_engine_get_default (),
IDE_TYPE_VCS_CLONER,
NULL);
g_signal_connect (self->addins,
"extension-added",
G_CALLBACK (ide_clone_surface_addin_added_cb),
self);
g_signal_connect (self->addins,
"extension-removed",
G_CALLBACK (ide_clone_surface_addin_removed_cb),
self);
peas_extension_set_foreach (self->addins,
ide_clone_surface_addin_added_cb,
self);
ide_clone_surface_update (self);
}
static void
ide_clone_surface_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
IdeCloneSurface *self = IDE_CLONE_SURFACE (object);
switch (prop_id)
{
case PROP_URI:
g_value_set_string (value, ide_clone_surface_get_uri (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
ide_clone_surface_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
IdeCloneSurface *self = IDE_CLONE_SURFACE (object);
switch (prop_id)
{