ide-configuration-manager.c 32.6 KB
Newer Older
1 2
/* ide-configuration-manager.c
 *
3
 * Copyright © 2016 Christian Hergert <chergert@redhat.com>
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#define G_LOG_DOMAIN "ide-configuration-manager"

21 22
#include "config.h"

23
#include <glib/gi18n.h>
24
#include <libpeas/peas.h>
25 26 27

#include "ide-context.h"
#include "ide-debug.h"
Christian Hergert's avatar
Christian Hergert committed
28

29
#include "application/ide-application.h"
30 31 32
#include "config/ide-configuration-manager.h"
#include "config/ide-configuration.h"
#include "config/ide-configuration-provider.h"
33 34
#include "buildconfig/ide-buildconfig-configuration.h"
#include "buildconfig/ide-buildconfig-configuration-provider.h"
35
#include "threading/ide-task.h"
36

37
#define WRITEBACK_DELAY_SEC 3
38

39 40 41 42
struct _IdeConfigurationManager
{
  GObject           parent_instance;

43 44
  GCancellable     *cancellable;
  GArray           *configs;
45
  IdeConfiguration *current;
46
  PeasExtensionSet *providers;
47
  GSettings        *project_settings;
48 49

  guint             queued_save_source;
50 51

  guint             propagate_to_settings : 1;
52 53
};

54 55 56 57 58 59
typedef struct
{
  IdeConfigurationProvider *provider;
  IdeConfiguration         *config;
} ConfigInfo;

60 61
static void async_initable_iface_init           (GAsyncInitableIface *iface);
static void list_model_iface_init               (GListModelInterface *iface);
62
static void ide_configuration_manager_save_tick (IdeTask             *task);
63 64 65 66 67 68 69 70 71

G_DEFINE_TYPE_EXTENDED (IdeConfigurationManager, ide_configuration_manager, IDE_TYPE_OBJECT, 0,
                        G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init)
                        G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init))

enum {
  PROP_0,
  PROP_CURRENT,
  PROP_CURRENT_DISPLAY_NAME,
72
  PROP_READY,
73 74 75
  LAST_PROP
};

76 77 78 79 80
enum {
  INVALIDATE,
  N_SIGNALS
};

81
static GParamSpec *properties [LAST_PROP];
82
static guint signals [N_SIGNALS];
83

84
static void
85
config_info_clear (gpointer data)
86
{
87
  ConfigInfo *info = data;
88

89 90
  g_clear_object (&info->config);
  g_clear_object (&info->provider);
91 92
}

93
static void
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
ide_configuration_manager_collect_providers (PeasExtensionSet *set,
                                             PeasPluginInfo   *plugin_info,
                                             PeasExtension    *exten,
                                             gpointer          user_data)
{
  IdeConfigurationProvider *provider = (IdeConfigurationProvider *)exten;
  GPtrArray *providers = user_data;

  g_assert (PEAS_IS_EXTENSION_SET (set));
  g_assert (plugin_info != NULL);
  g_assert (IDE_IS_CONFIGURATION_PROVIDER (provider));
  g_assert (providers != NULL);

  g_ptr_array_add (providers, g_object_ref (provider));
}

static void
ide_configuration_manager_save_cb (GObject      *object,
                                   GAsyncResult *result,
                                   gpointer      user_data)
114
{
115
  IdeConfigurationProvider *provider = (IdeConfigurationProvider *)object;
116
  g_autoptr(IdeTask) task = user_data;
117 118 119
  g_autoptr(GError) error = NULL;

  IDE_ENTRY;
120

121
  g_assert (IDE_IS_CONFIGURATION_PROVIDER (provider));
122
  g_assert (IDE_IS_TASK (task));
123

124
  if (!ide_configuration_provider_save_finish (provider, result, &error))
125 126 127 128 129
    g_warning ("%s: %s", G_OBJECT_TYPE_NAME (provider), error->message);

  ide_configuration_manager_save_tick (task);

  IDE_EXIT;
130 131 132
}

static void
133
ide_configuration_manager_save_tick (IdeTask *task)
134
{
135 136 137 138 139
  IdeConfigurationProvider *provider;
  GCancellable *cancellable;
  GPtrArray *providers;

  IDE_ENTRY;
140

141
  g_assert (IDE_IS_TASK (task));
142

143 144
  providers = ide_task_get_task_data (task);
  cancellable = ide_task_get_cancellable (task);
145 146 147

  if (providers->len == 0)
    {
148
      ide_task_return_boolean (task, TRUE);
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
      IDE_EXIT;
    }

  provider = g_ptr_array_index (providers, providers->len - 1);

  g_assert (IDE_IS_CONFIGURATION_PROVIDER (provider));

  ide_configuration_provider_save_async (provider,
                                         cancellable,
                                         ide_configuration_manager_save_cb,
                                         g_object_ref (task));

  g_ptr_array_remove_index (providers, providers->len - 1);

  IDE_EXIT;
164 165 166 167 168 169 170 171
}

void
ide_configuration_manager_save_async (IdeConfigurationManager *self,
                                      GCancellable            *cancellable,
                                      GAsyncReadyCallback      callback,
                                      gpointer                 user_data)
{
172
  g_autoptr(GPtrArray) providers = NULL;
173
  g_autoptr(IdeTask) task = NULL;
174 175 176 177 178 179

  IDE_ENTRY;

  g_assert (IDE_IS_CONFIGURATION_MANAGER (self));
  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));

180 181 182
  task = ide_task_new (self, cancellable, callback, user_data);
  ide_task_set_source_tag (task, ide_configuration_manager_save_async);
  ide_task_set_priority (task, G_PRIORITY_LOW);
183 184

  providers = g_ptr_array_new_with_free_func (g_object_unref);
185
  peas_extension_set_foreach (self->providers,
186 187
                              ide_configuration_manager_collect_providers,
                              providers);
188
  ide_task_set_task_data (task, g_ptr_array_ref (providers), (GDestroyNotify)g_ptr_array_unref);
189 190

  if (providers->len == 0)
191
    ide_task_return_boolean (task, TRUE);
192 193
  else
    ide_configuration_manager_save_tick (task);
194 195 196 197 198 199 200 201 202 203

  IDE_EXIT;
}

gboolean
ide_configuration_manager_save_finish (IdeConfigurationManager  *self,
                                       GAsyncResult             *result,
                                       GError                  **error)
{
  g_return_val_if_fail (IDE_IS_CONFIGURATION_MANAGER (self), FALSE);
204
  g_return_val_if_fail (IDE_IS_TASK (result), FALSE);
205

206
  return ide_task_propagate_boolean (IDE_TASK (result), error);
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
}

/**
 * ide_configuration_manager_get_configuration:
 * @self: An #IdeConfigurationManager
 * @id: The string identifier of the configuration
 *
 * Gets the #IdeConfiguration by id. See ide_configuration_get_id().
 *
 * Returns: (transfer none) (nullable): An #IdeConfiguration or %NULL if
 *   the configuration could not be found.
 */
IdeConfiguration *
ide_configuration_manager_get_configuration (IdeConfigurationManager *self,
                                             const gchar             *id)
{
  g_return_val_if_fail (IDE_IS_CONFIGURATION_MANAGER (self), NULL);
  g_return_val_if_fail (id != NULL, NULL);

226
  for (guint i = 0; i < self->configs->len; i++)
227
    {
228
      const ConfigInfo *info = &g_array_index (self->configs, ConfigInfo, i);
229

230
      g_assert (IDE_IS_CONFIGURATION (info->config));
231

232 233
      if (dzl_str_equal0 (id, ide_configuration_get_id (info->config)))
        return info->config;
234 235 236 237 238
    }

  return NULL;
}

239 240 241 242 243 244 245 246 247 248 249
static const gchar *
ide_configuration_manager_get_display_name (IdeConfigurationManager *self)
{
  g_return_val_if_fail (IDE_IS_CONFIGURATION_MANAGER (self), NULL);

  if (self->current != NULL)
    return ide_configuration_get_display_name (self->current);

  return "";
}

250 251 252 253 254 255 256 257 258 259 260
static void
ide_configuration_manager_notify_display_name (IdeConfigurationManager *self,
                                               GParamSpec              *pspec,
                                               IdeConfiguration        *configuration)
{
  g_assert (IDE_IS_CONFIGURATION_MANAGER (self));
  g_assert (IDE_IS_CONFIGURATION (configuration));

  g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CURRENT_DISPLAY_NAME]);
}

261 262 263 264 265 266 267 268 269 270 271
static void
ide_configuration_manager_notify_ready (IdeConfigurationManager *self,
                                        GParamSpec              *pspec,
                                        IdeConfiguration        *configuration)
{
  g_assert (IDE_IS_CONFIGURATION_MANAGER (self));
  g_assert (IDE_IS_CONFIGURATION (configuration));

  g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_READY]);
}

272 273 274 275 276
static void
ide_configuration_manager_dispose (GObject *object)
{
  IdeConfigurationManager *self = (IdeConfigurationManager *)object;

277
  if (self->current != NULL)
278 279 280 281 282 283 284 285
    {
      g_signal_handlers_disconnect_by_func (self->current,
                                            G_CALLBACK (ide_configuration_manager_notify_display_name),
                                            self);
      g_signal_handlers_disconnect_by_func (self->current,
                                            G_CALLBACK (ide_configuration_manager_notify_ready),
                                            self);
    }
286

287
  g_cancellable_cancel (self->cancellable);
288
  g_clear_object (&self->project_settings);
289 290 291 292

  G_OBJECT_CLASS (ide_configuration_manager_parent_class)->dispose (object);
}

293 294 295 296 297
static void
ide_configuration_manager_finalize (GObject *object)
{
  IdeConfigurationManager *self = (IdeConfigurationManager *)object;

298
  g_clear_object (&self->current);
299 300
  g_clear_object (&self->cancellable);
  g_clear_pointer (&self->configs, g_array_unref);
301

302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
  G_OBJECT_CLASS (ide_configuration_manager_parent_class)->finalize (object);
}

static void
ide_configuration_manager_get_property (GObject    *object,
                                        guint       prop_id,
                                        GValue     *value,
                                        GParamSpec *pspec)
{
  IdeConfigurationManager *self = IDE_CONFIGURATION_MANAGER (object);

  switch (prop_id)
    {
    case PROP_CURRENT:
      g_value_set_object (value, ide_configuration_manager_get_current (self));
      break;

    case PROP_CURRENT_DISPLAY_NAME:
320 321 322 323 324 325
      g_value_set_string (value, ide_configuration_manager_get_display_name (self));
      break;

    case PROP_READY:
      g_value_set_boolean (value, ide_configuration_manager_get_ready (self));
      break;
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
    }
}

static void
ide_configuration_manager_set_property (GObject      *object,
                                        guint         prop_id,
                                        const GValue *value,
                                        GParamSpec   *pspec)
{
  IdeConfigurationManager *self = IDE_CONFIGURATION_MANAGER (object);

  switch (prop_id)
    {
    case PROP_CURRENT:
      ide_configuration_manager_set_current (self, g_value_get_object (value));
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
    }
}

static void
ide_configuration_manager_class_init (IdeConfigurationManagerClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

356
  object_class->dispose = ide_configuration_manager_dispose;
357 358 359 360 361 362 363 364 365
  object_class->finalize = ide_configuration_manager_finalize;
  object_class->get_property = ide_configuration_manager_get_property;
  object_class->set_property = ide_configuration_manager_set_property;

  properties [PROP_CURRENT] =
    g_param_spec_object ("current",
                         "Current",
                         "The current configuration for the context",
                         IDE_TYPE_CONFIGURATION,
366
                         (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
367 368 369 370 371 372 373 374

  properties [PROP_CURRENT_DISPLAY_NAME] =
    g_param_spec_string ("current-display-name",
                         "Current Display Name",
                         "The display name of the current configuration",
                         NULL,
                         (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));

375 376 377 378 379 380 381
  properties [PROP_READY] =
    g_param_spec_boolean ("ready",
                          "Ready",
                          "If the current configuration is ready",
                          FALSE,
                          (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));

382
  g_object_class_install_properties (object_class, LAST_PROP, properties);
383 384 385

  /**
   * IdeConfigurationManager::invalidate:
386
   * @self: an #IdeConfigurationManager
387 388 389 390 391 392 393 394
   *
   * This signal is emitted any time a new configuration is selected or the
   * currently selected configurations state changes.
   */
  signals [INVALIDATE] =
    g_signal_new ("invalidate",
                  G_TYPE_FROM_CLASS (klass),
                  G_SIGNAL_RUN_LAST,
395 396 397
                  0, NULL, NULL,
                  g_cclosure_marshal_VOID__VOID,
                  G_TYPE_NONE, 0);
398 399 400 401 402
}

static void
ide_configuration_manager_init (IdeConfigurationManager *self)
{
403 404 405
  self->cancellable = g_cancellable_new ();
  self->configs = g_array_new (FALSE, FALSE, sizeof (ConfigInfo));
  g_array_set_clear_func (self->configs, config_info_clear);
406 407 408 409 410 411 412 413 414 415 416 417 418 419
}

static GType
ide_configuration_manager_get_item_type (GListModel *model)
{
  return IDE_TYPE_CONFIGURATION;
}

static guint
ide_configuration_manager_get_n_items (GListModel *model)
{
  IdeConfigurationManager *self = (IdeConfigurationManager *)model;

  g_assert (IDE_IS_CONFIGURATION_MANAGER (self));
420
  g_assert (self->configs != NULL);
421

422
  return self->configs->len;
423 424 425 426 427 428 429
}

static gpointer
ide_configuration_manager_get_item (GListModel *model,
                                    guint       position)
{
  IdeConfigurationManager *self = (IdeConfigurationManager *)model;
430
  const ConfigInfo *info;
431 432

  g_return_val_if_fail (IDE_IS_CONFIGURATION_MANAGER (self), NULL);
433 434 435
  g_return_val_if_fail (position < self->configs->len, NULL);

  info = &g_array_index (self->configs, ConfigInfo, position);
436

437
  return g_object_ref (info->config);
438 439 440 441 442 443 444 445 446 447
}

static void
list_model_iface_init (GListModelInterface *iface)
{
  iface->get_item_type = ide_configuration_manager_get_item_type;
  iface->get_n_items = ide_configuration_manager_get_n_items;
  iface->get_item = ide_configuration_manager_get_item;
}

448 449 450 451 452 453 454 455 456 457 458
static gboolean
ide_configuration_manager_do_save (gpointer data)
{
  IdeConfigurationManager *self = data;

  IDE_ENTRY;

  g_assert (IDE_IS_CONFIGURATION_MANAGER (self));

  self->queued_save_source = 0;

459 460
  g_signal_emit (self, signals [INVALIDATE], 0);

461 462 463 464 465
  ide_configuration_manager_save_async (self, NULL, NULL, NULL);

  IDE_RETURN (G_SOURCE_REMOVE);
}

466
static void
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
ide_configuration_manager_changed (IdeConfigurationManager *self,
                                   IdeConfiguration        *config)
{
  g_assert (IDE_IS_CONFIGURATION_MANAGER (self));
  g_assert (IDE_IS_CONFIGURATION (config));

  dzl_clear_source (&self->queued_save_source);
  self->queued_save_source =
    g_timeout_add_seconds_full (G_PRIORITY_LOW,
                                WRITEBACK_DELAY_SEC,
                                ide_configuration_manager_do_save,
                                g_object_ref (self),
                                g_object_unref);
}

static void
ide_configuration_manager_config_added (IdeConfigurationManager  *self,
                                        IdeConfiguration         *config,
                                        IdeConfigurationProvider *provider)
{
  ConfigInfo info = {0};

  IDE_ENTRY;

  g_assert (IDE_IS_CONFIGURATION_MANAGER (self));
  g_assert (IDE_IS_CONFIGURATION (config));
  g_assert (IDE_IS_CONFIGURATION_PROVIDER (provider));

  g_signal_connect_object (config,
                           "changed",
                           G_CALLBACK (ide_configuration_manager_changed),
                           self,
                           G_CONNECT_SWAPPED);

  info.provider = g_object_ref (provider);
  info.config = g_object_ref (config);
  g_array_append_val (self->configs, info);

  g_list_model_items_changed (G_LIST_MODEL (self), self->configs->len - 1, 0, 1);

  if (self->current == NULL)
    ide_configuration_manager_set_current (self, config);

  IDE_EXIT;
}

static void
ide_configuration_manager_config_removed (IdeConfigurationManager  *self,
                                          IdeConfiguration         *config,
                                          IdeConfigurationProvider *provider)
{
  IDE_ENTRY;

  g_assert (IDE_IS_CONFIGURATION_MANAGER (self));
  g_assert (IDE_IS_CONFIGURATION (config));
  g_assert (IDE_IS_CONFIGURATION_PROVIDER (provider));

  for (guint i = 0; i < self->configs->len; i++)
    {
      const ConfigInfo *info = &g_array_index (self->configs, ConfigInfo, i);

      if (info->provider == provider && info->config == config)
        {
          g_signal_handlers_disconnect_by_func (config,
                                                G_CALLBACK (ide_configuration_manager_changed),
                                                self);
          g_array_remove_index (self->configs, i);
          g_list_model_items_changed (G_LIST_MODEL (self), i, 1, 0);
          break;
        }
    }

  IDE_EXIT;
}

static void
ide_configuration_manager_provider_load_cb (GObject      *object,
                                            GAsyncResult *result,
                                            gpointer      user_data)
546 547
{
  IdeConfigurationProvider *provider = (IdeConfigurationProvider *)object;
548
  IdeContext *context;
549
  g_autoptr(IdeConfigurationManager) self = user_data;
550 551 552 553 554 555
  g_autoptr(GError) error = NULL;

  IDE_ENTRY;

  g_assert (IDE_IS_CONFIGURATION_PROVIDER (provider));
  g_assert (IDE_IS_CONFIGURATION_MANAGER (self));
556
  g_assert (IDE_IS_TASK (result));
557

558 559
  context = ide_object_get_context (IDE_OBJECT (self));

560
  if (!ide_configuration_provider_load_finish (provider, result, &error))
561 562 563
    ide_context_warning (context,
                         "Failed to initialize config provider: %s: %s",
                         G_OBJECT_TYPE_NAME (provider), error->message);
564 565 566 567

  IDE_EXIT;
}

568
static void
569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606
provider_connect (IdeConfigurationManager  *self,
                  IdeConfigurationProvider *provider)
{
  g_assert (IDE_IS_CONFIGURATION_MANAGER (self));
  g_assert (IDE_IS_CONFIGURATION_PROVIDER (provider));

  g_signal_connect_object (provider,
                           "added",
                           G_CALLBACK (ide_configuration_manager_config_added),
                           self,
                           G_CONNECT_SWAPPED);
  g_signal_connect_object (provider,
                           "removed",
                           G_CALLBACK (ide_configuration_manager_config_removed),
                           self,
                           G_CONNECT_SWAPPED);
}

static void
provider_disconnect (IdeConfigurationManager  *self,
                     IdeConfigurationProvider *provider)
{
  g_assert (IDE_IS_CONFIGURATION_MANAGER (self));
  g_assert (IDE_IS_CONFIGURATION_PROVIDER (provider));

  g_signal_handlers_disconnect_by_func (provider,
                                        G_CALLBACK (ide_configuration_manager_config_added),
                                        self);
  g_signal_handlers_disconnect_by_func (provider,
                                        G_CALLBACK (ide_configuration_manager_config_removed),
                                        self);
}

static void
ide_configuration_manager_provider_added (PeasExtensionSet *set,
                                          PeasPluginInfo   *plugin_info,
                                          PeasExtension    *exten,
                                          gpointer          user_data)
607 608 609 610
{
  IdeConfigurationManager *self = user_data;
  IdeConfigurationProvider *provider = (IdeConfigurationProvider *)exten;

611
  g_assert (IDE_IS_MAIN_THREAD ());
612 613 614 615
  g_assert (PEAS_IS_EXTENSION_SET (set));
  g_assert (plugin_info != NULL);
  g_assert (IDE_IS_CONFIGURATION_PROVIDER (provider));

616 617
  provider_connect (self, provider);

618
  ide_configuration_provider_load_async (provider,
619 620
                                         self->cancellable,
                                         ide_configuration_manager_provider_load_cb,
621
                                         g_object_ref (self));
622 623 624
}

static void
625 626 627 628
ide_configuration_manager_provider_removed (PeasExtensionSet *set,
                                            PeasPluginInfo   *plugin_info,
                                            PeasExtension    *exten,
                                            gpointer          user_data)
629 630 631
{
  IdeConfigurationManager *self = user_data;
  IdeConfigurationProvider *provider = (IdeConfigurationProvider *)exten;
632
  g_autoptr(IdeConfigurationProvider) hold = NULL;
633

634
  g_assert (IDE_IS_MAIN_THREAD ());
635 636 637 638
  g_assert (PEAS_IS_EXTENSION_SET (set));
  g_assert (plugin_info != NULL);
  g_assert (IDE_IS_CONFIGURATION_PROVIDER (provider));

639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656
  hold = g_object_ref (provider);

  ide_configuration_provider_unload (provider);

  provider_disconnect (self, provider);

  for (guint i = self->configs->len; i > 0; i--)
    {
      const ConfigInfo *info = &g_array_index (self->configs, ConfigInfo, i - 1);

      if (info->provider == provider)
        {
          g_warning ("%s failed to remove configuration \"%s\"",
                     G_OBJECT_TYPE_NAME (provider),
                     ide_configuration_get_id (info->config));
          g_array_remove_index (self->configs, i);
        }
    }
657 658
}

659 660 661
static void
notify_providers_loaded (IdeConfigurationManager *self,
                         GParamSpec              *pspec,
662
                         IdeTask                 *task)
663 664 665 666
{
  g_autoptr(GVariant) user_value = NULL;

  g_assert (IDE_IS_CONFIGURATION_MANAGER (self));
667
  g_assert (IDE_IS_TASK (task));
668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705

  if (self->project_settings == NULL)
    return;

  /*
   * At this point, all of our configuratin providers have returned from
   * their asynchronous loading. So we should have all of the configs we
   * can know about at this point.
   *
   * We need to read our config-id from project_settings, and if we find
   * a match, make that our active configuration.
   *
   * We want to avoid applying the value if the value is unchanged
   * according to g_settings_get_user_value() so that we don't override
   * any provider that set_current() during it's load, unless the user
   * has manually set this config in the past.
   *
   * Once we have updated the current config, we can start propagating
   * new values to the settings when set_current() is called.
   */

  user_value = g_settings_get_user_value (self->project_settings, "config-id");

  if (user_value != NULL)
    {
      const gchar *str = g_variant_get_string (user_value, NULL);
      IdeConfiguration *config;

      if ((config = ide_configuration_manager_get_configuration (self, str)))
        {
          if (config != self->current)
            ide_configuration_manager_set_current (self, config);
        }
    }

  self->propagate_to_settings = TRUE;
}

706
static void
707 708 709
ide_configuration_manager_init_load_cb (GObject      *object,
                                        GAsyncResult *result,
                                        gpointer      user_data)
710
{
711 712 713
  IdeConfigurationProvider *provider = (IdeConfigurationProvider *)object;
  IdeConfigurationManager *self;
  g_autoptr(GError) error = NULL;
714
  g_autoptr(IdeTask) task = user_data;
715
  GPtrArray *providers;
716
  IdeContext *context;
717 718

  IDE_ENTRY;
719

720
  g_assert (IDE_IS_MAIN_THREAD ());
721 722
  g_assert (IDE_IS_CONFIGURATION_PROVIDER (provider));
  g_assert (G_IS_ASYNC_RESULT (result));
723
  g_assert (IDE_IS_TASK (task));
724

725
  self = ide_task_get_source_object (task);
726
  g_assert (IDE_IS_CONFIGURATION_MANAGER (self));
727

728 729 730
  context = ide_object_get_context (IDE_OBJECT (self));
  g_assert (IDE_IS_CONTEXT (context));

731
  if (!ide_configuration_provider_load_finish (provider, result, &error))
732 733 734 735 736 737 738
    {
      g_print ("%s\n", G_OBJECT_TYPE_NAME (provider));
      g_assert (error != NULL);
      ide_context_warning (context,
                           "Failed to initialize config provider: %s: %s",
                           G_OBJECT_TYPE_NAME (provider), error->message);
    }
739

740
  providers = ide_task_get_task_data (task);
741 742 743
  g_assert (providers != NULL);
  g_assert (providers->len > 0);

744 745
  if (!g_ptr_array_remove (providers, provider))
    g_critical ("Failed to locate provider in active set");
746 747

  if (providers->len == 0)
748
    ide_task_return_boolean (task, TRUE);
749 750 751 752 753 754 755 756 757 758 759 760 761

  IDE_EXIT;
}

static void
ide_configuration_manager_init_async (GAsyncInitable      *initable,
                                      gint                 priority,
                                      GCancellable        *cancellable,
                                      GAsyncReadyCallback  callback,
                                      gpointer             user_data)
{
  IdeConfigurationManager *self = (IdeConfigurationManager *)initable;
  g_autoptr(GPtrArray) providers = NULL;
762
  g_autoptr(IdeTask) task = NULL;
763 764 765
  IdeContext *context;

  g_assert (G_IS_ASYNC_INITABLE (self));
766 767
  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));

768 769 770
  task = ide_task_new (self, cancellable, callback, user_data);
  ide_task_set_source_tag (task, ide_configuration_manager_init_async);
  ide_task_set_priority (task, priority);
771

772 773 774 775 776
  g_signal_connect_swapped (task,
                            "notify::completed",
                            G_CALLBACK (notify_providers_loaded),
                            self);

777
  context = ide_object_get_context (IDE_OBJECT (self));
778
  g_assert (IDE_IS_CONTEXT (context));
779

780 781
  self->project_settings = ide_context_get_project_settings (context);

782 783 784 785
  self->providers = peas_extension_set_new (peas_engine_get_default (),
                                            IDE_TYPE_CONFIGURATION_PROVIDER,
                                            "context", context,
                                            NULL);
786

787
  g_signal_connect (self->providers,
788
                    "extension-added",
789
                    G_CALLBACK (ide_configuration_manager_provider_added),
790 791
                    self);

792
  g_signal_connect (self->providers,
793
                    "extension-removed",
794
                    G_CALLBACK (ide_configuration_manager_provider_removed),
795 796
                    self);

797
  providers = g_ptr_array_new_with_free_func (g_object_unref);
798
  peas_extension_set_foreach (self->providers,
799 800
                              ide_configuration_manager_collect_providers,
                              providers);
801
  ide_task_set_task_data (task, g_ptr_array_ref (providers), (GDestroyNotify)g_ptr_array_unref);
802

803 804 805
  for (guint i = 0; i < providers->len; i++)
    {
      IdeConfigurationProvider *provider = g_ptr_array_index (providers, i);
806

807 808 809 810
      g_assert (IDE_IS_CONFIGURATION_PROVIDER (provider));

      provider_connect (self, provider);

811 812 813 814 815
      ide_configuration_provider_load_async (provider,
                                             cancellable,
                                             ide_configuration_manager_init_load_cb,
                                             g_object_ref (task));
    }
816

817
  if (providers->len == 0)
818
    ide_task_return_boolean (task, TRUE);
819 820 821 822 823 824 825
}

static gboolean
ide_configuration_manager_init_finish (GAsyncInitable  *initable,
                                       GAsyncResult    *result,
                                       GError         **error)
{
826
  g_assert (IDE_IS_MAIN_THREAD ());
827
  g_assert (IDE_IS_CONFIGURATION_MANAGER (initable));
828
  g_assert (IDE_IS_TASK (result));
829

830
  return ide_task_propagate_boolean (IDE_TASK (result), error);
831 832 833 834 835 836 837 838 839 840 841 842 843
}

static void
async_initable_iface_init (GAsyncInitableIface *iface)
{
  iface->init_async = ide_configuration_manager_init_async;
  iface->init_finish = ide_configuration_manager_init_finish;
}

void
ide_configuration_manager_set_current (IdeConfigurationManager *self,
                                       IdeConfiguration        *current)
{
844
  g_return_if_fail (IDE_IS_MAIN_THREAD ());
845 846 847
  g_return_if_fail (IDE_IS_CONFIGURATION_MANAGER (self));
  g_return_if_fail (!current || IDE_IS_CONFIGURATION (current));

848
  if (self->current != current)
849
    {
850 851 852 853 854
      if (self->current != NULL)
        {
          g_signal_handlers_disconnect_by_func (self->current,
                                                G_CALLBACK (ide_configuration_manager_notify_display_name),
                                                self);
855 856 857
          g_signal_handlers_disconnect_by_func (self->current,
                                                G_CALLBACK (ide_configuration_manager_notify_ready),
                                                self);
858 859 860 861 862 863
          g_clear_object (&self->current);
        }

      if (current != NULL)
        {
          self->current = g_object_ref (current);
864

865 866 867 868 869
          g_signal_connect_object (current,
                                   "notify::display-name",
                                   G_CALLBACK (ide_configuration_manager_notify_display_name),
                                   self,
                                   G_CONNECT_SWAPPED);
870 871 872 873 874
          g_signal_connect_object (current,
                                   "notify::ready",
                                   G_CALLBACK (ide_configuration_manager_notify_ready),
                                   self,
                                   G_CONNECT_SWAPPED);
875 876 877 878 879 880

          if (self->propagate_to_settings && self->project_settings != NULL)
            {
              g_autofree gchar *new_id = g_strdup (ide_configuration_get_id (current));
              g_settings_set_string (self->project_settings, "config-id", new_id);
            }
881 882
        }

883 884
      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CURRENT]);
      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CURRENT_DISPLAY_NAME]);
885
      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_READY]);
886 887

      g_signal_emit (self, signals [INVALIDATE], 0);
888 889 890 891 892 893 894 895 896 897
    }
}

/**
 * ide_configuration_manager_get_current:
 * @self: An #IdeConfigurationManager
 *
 * Gets the current configuration to use for building.
 *
 * Many systems allow you to pass a configuration in instead of relying on the
898
 * default configuration. This gets the default configuration that various
899 900 901 902 903 904 905 906 907
 * background items might use, such as tags builders which need to discover
 * settings.
 *
 * Returns: (transfer none): An #IdeConfiguration
 */
IdeConfiguration *
ide_configuration_manager_get_current (IdeConfigurationManager *self)
{
  g_return_val_if_fail (IDE_IS_CONFIGURATION_MANAGER (self), NULL);
908
  g_return_val_if_fail (self->current != NULL || self->configs->len > 0, NULL);
909

910 911
  if (self->current != NULL)
    return self->current;
912

913 914 915
  if (self->configs->len > 0)
    {
      const ConfigInfo *info = &g_array_index (self->configs, ConfigInfo, 0);
916

917 918
      g_assert (IDE_IS_CONFIGURATION_PROVIDER (info->provider));
      g_assert (IDE_IS_CONFIGURATION (info->config));
919

920 921 922 923 924 925
      return info->config;
    }

  g_critical ("Failed to locate activate configuration. This should not happen.");

  return NULL;
926 927 928
}

void
929 930
ide_configuration_manager_duplicate (IdeConfigurationManager *self,
                                     IdeConfiguration        *config)
931 932
{
  g_return_if_fail (IDE_IS_CONFIGURATION_MANAGER (self));
933
  g_return_if_fail (IDE_IS_CONFIGURATION (config));
934

935
  for (guint i = 0; i < self->configs->len; i++)
936
    {
937
      const ConfigInfo *info = &g_array_index (self->configs, ConfigInfo, i);
938

939 940
      g_assert (IDE_IS_CONFIGURATION_PROVIDER (info->provider));
      g_assert (IDE_IS_CONFIGURATION (info->config));
941

942 943 944
      if (info->config == config)
        {
          g_autoptr(IdeConfigurationProvider) provider = g_object_ref (info->provider);
945

946 947 948 949 950
          info = NULL; /* info becomes invalid */
          ide_configuration_provider_duplicate (provider, config);
          ide_configuration_provider_save_async (provider, NULL, NULL, NULL);
          break;
        }
951
    }
952 953 954
}

void
955 956
ide_configuration_manager_delete (IdeConfigurationManager *self,
                                  IdeConfiguration        *config)
957
{
958
  g_autoptr(IdeConfiguration) hold = NULL;
959 960

  g_return_if_fail (IDE_IS_CONFIGURATION_MANAGER (self));
961 962 963
  g_return_if_fail (IDE_IS_CONFIGURATION (config));

  hold = g_object_ref (config);
964

965
  for (guint i = 0; i < self->configs->len; i++)
966
    {
967 968
      const ConfigInfo *info = &g_array_index (self->configs, ConfigInfo, i);
      g_autoptr(IdeConfigurationProvider) provider = NULL;
969

970 971 972 973 974 975
      g_assert (IDE_IS_CONFIGURATION_PROVIDER (info->provider));
      g_assert (IDE_IS_CONFIGURATION (info->config));

      provider = g_object_ref (info->provider);

      if (info->config == config)
976
        {
977 978 979
          info = NULL; /* info becomes invalid */
          ide_configuration_provider_delete (provider, config);
          ide_configuration_provider_save_async (provider, NULL, NULL, NULL);
980 981 982 983
          break;
        }
    }
}
984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011

/**
 * ide_configuration_manager_get_ready:
 * @self: an #IdeConfigurationManager
 *
 * This returns %TRUE if the current configuration is ready for usage.
 *
 * This is equivalent to checking the ready property of the current
 * configuration. It allows consumers to not need to track changes to
 * the current configuration.
 *
 * Returns: %TRUE if the current configuration is ready for usage;
 *   otherwise %FALSE.
 *
 * Since: 3.28
 */
gboolean
ide_configuration_manager_get_ready (IdeConfigurationManager *self)
{
  IdeConfiguration *config;

  g_return_val_if_fail (IDE_IS_CONFIGURATION_MANAGER (self), FALSE);

  if ((config = ide_configuration_manager_get_current (self)))
    return ide_configuration_get_ready (config);

  return FALSE;
}