glade-app.c 23.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/*
 * Copyright (C) 2001 Ximian, Inc.
 *
 * 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
16
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 18 19 20 21 22 23
 *
 * Authors:
 *   Naba Kumar <naba@gnome.org>
 */

#include <config.h>

24 25 26 27 28 29 30 31 32 33
/**
 * SECTION:glade-app
 * @Short_Description: The central control point of the Glade core.
 *
 * This main control object is where we try to draw the line between
 * what is the Glade core and what is the main application. The main
 * application must derive from the GladeApp object and create an instance
 * to initialize the Glade core.
 */

34 35 36 37
#include "glade.h"
#include "glade-debug.h"
#include "glade-cursor.h"
#include "glade-catalog.h"
38
#include "glade-design-view.h"
39
#include "glade-design-layout.h"
40
#include "glade-marshallers.h"
41
#include "glade-accumulators.h"
42

43 44 45 46
#include <string.h>
#include <glib.h>
#include <glib/gstdio.h>
#include <glib/gi18n-lib.h>
47
#include <gdk/gdkkeysyms.h>
48
#include <gtk/gtk.h>
49

50
#ifdef MAC_INTEGRATION
51
#  include <gtkosxapplication.h>
52 53
#endif

54
#define GLADE_CONFIG_FILENAME "glade.conf"
55

56 57
enum
{
58
  DOC_SEARCH,
59
  SIGNAL_EDITOR_CREATED,
60
  WIDGET_ADAPTOR_REGISTERED,
61
  LAST_SIGNAL
62 63 64 65
};

struct _GladeAppPrivate
{
66 67 68 69 70 71
  GtkWidget *window;

  GladeClipboard *clipboard;    /* See glade-clipboard */
  GList *catalogs;              /* See glade-catalog */

  GList *projects;              /* The list of Projects */
72

73
  GKeyFile *config;             /* The configuration file */
74

75
  GtkAccelGroup *accel_group;   /* Default acceleration group for this app */
76 77 78 79
};

static guint glade_app_signals[LAST_SIGNAL] = { 0 };

80 81
/* installation paths */
static gchar *catalogs_dir = NULL;
82 83 84 85
static gchar *modules_dir = NULL;
static gchar *pixmaps_dir = NULL;
static gchar *locale_dir = NULL;
static gchar *bin_dir = NULL;
86
static gchar *lib_dir = NULL;
87

88
static GladeApp *singleton_app = NULL;
89
static gboolean check_initialised = FALSE;
90

91
G_DEFINE_TYPE_WITH_PRIVATE (GladeApp, glade_app, G_TYPE_OBJECT);
Tristan Van Berkom's avatar
Tristan Van Berkom committed
92 93 94 95 96

/*****************************************************************
 *                    GObjectClass                               *
 *****************************************************************/
static GObject *
97 98 99
glade_app_constructor (GType                  type,
                       guint                  n_construct_properties,
                       GObjectConstructParam *construct_properties)
Tristan Van Berkom's avatar
Tristan Van Berkom committed
100
{
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
  GObject *object;

  /* singleton */
  if (!singleton_app)
    {
      object = G_OBJECT_CLASS (glade_app_parent_class)->constructor (type,
                                                                     n_construct_properties,
                                                                     construct_properties);
      singleton_app = GLADE_APP (object);
    }
  else
    {
      g_object_ref (singleton_app);
    }

  return G_OBJECT (singleton_app);
Tristan Van Berkom's avatar
Tristan Van Berkom committed
117 118 119
}


120 121

static void
122
glade_app_dispose (GObject *app)
123
{
124
  GladeAppPrivate *priv = GLADE_APP (app)->priv;
125 126 127

  if (priv->clipboard)
    {
128
      g_object_unref (priv->clipboard);
129 130 131 132 133 134 135 136 137 138 139
      priv->clipboard = NULL;
    }
  /* FIXME: Remove projects */

  if (priv->config)
    {
      g_key_file_free (priv->config);
      priv->config = NULL;
    }

  G_OBJECT_CLASS (glade_app_parent_class)->dispose (app);
140 141 142
}

static void
143
glade_app_finalize (GObject *app)
144
{
145 146 147 148 149
  g_free (catalogs_dir);
  g_free (modules_dir);
  g_free (pixmaps_dir);
  g_free (locale_dir);
  g_free (bin_dir);
150
  g_free (lib_dir);
151

152 153
  singleton_app = NULL;
  check_initialised = FALSE;
154

155
  G_OBJECT_CLASS (glade_app_parent_class)->finalize (app);
156 157
}

158 159 160 161
/* build package paths at runtime */
static void
build_package_paths (void)
{
162 163 164 165 166 167
  const gchar *path;

  path = g_getenv (GLADE_ENV_PIXMAP_DIR);
  if (path)
    pixmaps_dir = g_strdup (path);

168
#if defined (G_OS_WIN32) || (defined (MAC_INTEGRATION) && defined (MAC_BUNDLE))
169
  gchar *prefix;
170 171

# ifdef G_OS_WIN32
172
  prefix = g_win32_get_package_installation_directory_of_module (NULL);
173 174

# else // defined (MAC_INTEGRATION) && defined (MAC_BUNDLE)
175
  prefix = quartz_application_get_resource_path ();
176

177
# endif
178

179 180 181
  if (!pixmaps_dir)
    pixmaps_dir = g_build_filename (prefix, "share", PACKAGE, "pixmaps", NULL);

182 183 184 185
  catalogs_dir = g_build_filename (prefix, "share", PACKAGE, "catalogs", NULL);
  modules_dir = g_build_filename (prefix, "lib", PACKAGE, "modules", NULL);
  locale_dir = g_build_filename (prefix, "share", "locale", NULL);
  bin_dir = g_build_filename (prefix, "bin", NULL);
186
  lib_dir = g_build_filename (prefix, "lib", NULL);
187

188
  g_free (prefix);
189
#else
190 191
  catalogs_dir = g_strdup (GLADE_CATALOGSDIR);
  modules_dir = g_strdup (GLADE_MODULESDIR);
192 193 194

  if (!pixmaps_dir)
    pixmaps_dir = g_strdup (GLADE_PIXMAPSDIR);
195 196
  locale_dir = g_strdup (GLADE_LOCALEDIR);
  bin_dir = g_strdup (GLADE_BINDIR);
197
  lib_dir = g_strdup (GLADE_LIBDIR);
198 199 200 201 202 203
#endif
}

/* initialization function for libgladeui (not GladeApp) */
static void
glade_init_check (void)
204 205 206
{
  if (check_initialised)
    return;
207

208 209
  glade_init_debug_flags ();

210 211
  /* Make sure path accessors work on osx */
  build_package_paths ();
212

213 214
  bindtextdomain (GETTEXT_PACKAGE, locale_dir);
  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
215

216
  check_initialised = TRUE;
217 218
}

Juan Pablo Ugarte's avatar
Juan Pablo Ugarte committed
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
/*****************************************************************
 *                    GladeAppClass                              *
 *****************************************************************/
const gchar *
glade_app_get_catalogs_dir (void)
{
  glade_init_check ();

  return catalogs_dir;
}

const gchar *
glade_app_get_modules_dir (void)
{
  glade_init_check ();

  return modules_dir;
}

const gchar *
glade_app_get_pixmaps_dir (void)
{
  glade_init_check ();

  return pixmaps_dir;
}

const gchar *
glade_app_get_locale_dir (void)
{
  glade_init_check ();

  return locale_dir;
}

const gchar *
glade_app_get_bin_dir (void)
{
  glade_init_check ();

  return bin_dir;
}

262 263 264 265 266 267 268 269
const gchar *
glade_app_get_lib_dir (void)
{
  glade_init_check ();

  return lib_dir;
}

270
static void
271 272
pointer_mode_register_icon (const gchar     *icon_name,
                            gint             real_size,
273
                            GladePointerMode mode,
274
                            GtkIconSize      size)
275 276 277
{
  GdkPixbuf *pixbuf;

278
  if ((pixbuf = glade_utils_pointer_mode_render_icon (mode, size)))
279
    {
280
      gtk_icon_theme_add_builtin_icon (icon_name, real_size, pixbuf);
281 282 283 284
      g_object_unref (pixbuf);
    }
}

285
static void
286 287 288 289
register_icon (const gchar    *new_icon_name,
               gint            size,
               const gchar    *icon_name,
               const gchar    *file_name)
290 291 292 293 294
{
  GtkIconTheme *icon_theme = gtk_icon_theme_get_default ();
  GdkPixbuf *pixbuf;
  GtkIconInfo *info;

295
  if ((info = gtk_icon_theme_lookup_icon (icon_theme, icon_name, size, 0)))
296 297 298 299 300 301 302 303 304 305 306 307
    {
      pixbuf = gtk_icon_info_load_icon (info, NULL);
    }
  else
    {
      gchar *path = g_build_filename (glade_app_get_pixmaps_dir (), file_name, NULL);
      pixbuf = gdk_pixbuf_new_from_file (path, NULL);
      g_free (path);
    }

  if (pixbuf)
    {
308
      gtk_icon_theme_add_builtin_icon (new_icon_name, size, pixbuf);
309 310 311 312
      g_object_unref (pixbuf);
    }
}

313
/*
314
 * glade_app_register_icon_names:
315 316
 * @size: icon size
 *
317
 * Register a new icon name for most of GladePointerMode.
318
 * After calling this function "glade-selector", "glade-drag-resize",
319
 * "glade-margin-edit" and "glade-align-edit" icon names will be available.
320 321
 */ 
static void
322
glade_app_register_icon_names (GtkIconSize size)
323
{
324
  gint w, h, real_size;
325

326 327
  if (gtk_icon_size_lookup (size, &w, &h) == FALSE)
    return;
328

329 330 331 332 333 334 335 336 337 338
  real_size = MIN (w, h);

  pointer_mode_register_icon ("glade-selector", real_size, GLADE_POINTER_SELECT, size);
  pointer_mode_register_icon ("glade-drag-resize", real_size, GLADE_POINTER_DRAG_RESIZE, size);
  pointer_mode_register_icon ("glade-margin-edit", real_size, GLADE_POINTER_MARGIN_EDIT, size);
  pointer_mode_register_icon ("glade-align-edit", real_size, GLADE_POINTER_ALIGN_EDIT, size);

  register_icon ("glade-devhelp", real_size,
                 GLADE_DEVHELP_ICON_NAME,
                 GLADE_DEVHELP_FALLBACK_ICON_FILE);
339 340
}

Juan Pablo Ugarte's avatar
Juan Pablo Ugarte committed
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
/**
 * glade_init:
 * 
 * Initialization function for libgladeui (not #GladeApp)
 * It builds paths, bind text domain, and register icons
 */
void
glade_init (void)
{
  static gboolean init = FALSE;

  if (init) return;
  
  glade_init_check ();

  /* Register icons needed by the UI */
357
  glade_app_register_icon_names (GTK_ICON_SIZE_LARGE_TOOLBAR);
358
  
Juan Pablo Ugarte's avatar
Juan Pablo Ugarte committed
359 360 361
  init = TRUE;
}

Tristan Van Berkom's avatar
Tristan Van Berkom committed
362
static void
Juan Pablo Ugarte's avatar
Juan Pablo Ugarte committed
363
glade_app_init (GladeApp *app)
Tristan Van Berkom's avatar
Tristan Van Berkom committed
364
{
365
  static gboolean initialized = FALSE;
366
  GladeAppPrivate *priv = app->priv = glade_app_get_instance_private (app);
367

368 369
  singleton_app = app;

Juan Pablo Ugarte's avatar
Juan Pablo Ugarte committed
370
  glade_init ();
371 372 373

  if (!initialized)
    {
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
      GtkIconTheme *default_icon_theme = gtk_icon_theme_get_default ();
      const gchar *path;

      gtk_icon_theme_append_search_path (default_icon_theme, pixmaps_dir);

      /* Handle extra icon theme paths. Needed for tests to work */
      if ((path = g_getenv (GLADE_ENV_ICON_THEME_PATH)))
        {
          gchar **tokens = g_strsplit (path, ":", -1);
          gint i;

          for (i = 0; tokens[i]; i++)
            gtk_icon_theme_append_search_path (default_icon_theme, tokens[i]);

          g_strfreev (tokens);
        }
390 391 392 393 394 395

      glade_cursor_init ();

      initialized = TRUE;
    }

396
  priv->accel_group = NULL;
397 398

  /* Initialize app objects */
399
  priv->catalogs = (GList *) glade_catalog_load_all ();
400 401

  /* Create clipboard */
402
  priv->clipboard = glade_clipboard_new ();
403 404

  /* Load the configuration file */
Juan Pablo Ugarte's avatar
Juan Pablo Ugarte committed
405
  priv->config = g_key_file_ref (glade_app_get_config ());
Tristan Van Berkom's avatar
Tristan Van Berkom committed
406 407
}

408 409 410 411 412 413 414 415
static void
glade_app_event_handler (GdkEvent *event, gpointer data)
{
  if (glade_app_do_event (event)) return;

  gtk_main_do_event (event);
}

Tristan Van Berkom's avatar
Tristan Van Berkom committed
416
static void
417
glade_app_class_init (GladeAppClass *klass)
Tristan Van Berkom's avatar
Tristan Van Berkom committed
418
{
419
  GObjectClass *object_class;
420

421
  object_class = G_OBJECT_CLASS (klass);
422 423 424 425 426

  object_class->constructor = glade_app_constructor;
  object_class->dispose = glade_app_dispose;
  object_class->finalize = glade_app_finalize;

427 428 429 430 431 432 433 434 435 436 437 438 439
  /**
   * GladeApp::doc-search:
   * @gladeeditor: the #GladeEditor which received the signal.
   * @arg1: the (#gchar *) book to search or %NULL
   * @arg2: the (#gchar *) page to search or %NULL
   * @arg3: the (#gchar *) search string or %NULL
   *
   * Emitted when the glade core requests that a doc-search be performed.
   */
  glade_app_signals[DOC_SEARCH] =
      g_signal_new ("doc-search",
                    G_TYPE_FROM_CLASS (object_class),
                    G_SIGNAL_RUN_LAST, 0, NULL, NULL,
440
                    _glade_marshal_VOID__STRING_STRING_STRING,
441 442
                    G_TYPE_NONE, 3,
                    G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
443

444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
  /**
   * GladeApp::signal-editor-created:
   * @gladeapp: the #GladeApp which received the signal.
   * @signal_editor: the new #GladeSignalEditor.
   *
   * Emitted when a new signal editor created.
   * A tree view is created in the default handler.
   * Connect your handler before the default handler for setting a custom column or renderer
   * and after it for connecting to the tree view signals
   */
  glade_app_signals[SIGNAL_EDITOR_CREATED] =
    g_signal_new ("signal-editor-created",
                  G_TYPE_FROM_CLASS (object_class),
                  G_SIGNAL_RUN_LAST,
                  0, NULL, NULL,
459
                  _glade_marshal_VOID__OBJECT,
460 461
                  G_TYPE_NONE, 1, G_TYPE_OBJECT);  

462 463 464 465 466 467 468 469 470 471 472 473 474
  /**
   * GladeApp::widget-adaptor-registered:
   * @gladeapp: the #GladeApp which received the signal.
   * @adaptor: the newlly registered #GladeWidgetAdaptor.
   *
   * Emitted when a new widget adaptor is registered.
   */
  glade_app_signals[WIDGET_ADAPTOR_REGISTERED] =
    g_signal_new ("widget-adaptor-registered",
                  G_TYPE_FROM_CLASS (object_class),
                  G_SIGNAL_RUN_LAST,
                  0, NULL, NULL,
                  _glade_marshal_VOID__OBJECT,
475
                  G_TYPE_NONE, 1, G_TYPE_OBJECT);
476 477

  gdk_event_handler_set (glade_app_event_handler, NULL, NULL);
Tristan Van Berkom's avatar
Tristan Van Berkom committed
478 479 480 481 482 483
}

/*****************************************************************
 *                       Public API                              *
 *****************************************************************/

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
/**
 * glade_app_do_event:
 * @event: the event to process.
 *
 * This function has to be called in an event handler for widget selection to work.
 * See gdk_event_handler_set()
 *
 * Returns: true if the event was handled.
 */ 
gboolean
glade_app_do_event (GdkEvent *event)
{
  GdkWindow *window = event->any.window;
  GtkWidget *layout;
  gpointer widget;

  if (window == NULL) return FALSE;
    
  gdk_window_get_user_data (window, &widget);

  /* As a slight optimization we could replace gtk_widget_get_ancestor()
   * with a custom function that only iterates trought parents with windows.
   */
  if (widget && IS_GLADE_WIDGET_EVENT (event->type) &&
      (layout = gtk_widget_get_ancestor (widget, GLADE_TYPE_DESIGN_LAYOUT)))
509
    return _glade_design_layout_do_event (GLADE_DESIGN_LAYOUT (layout), event);
510 511 512 513

  return FALSE;
}

514 515 516
/**
 * glade_app_config_save
 *
517
 * Saves the #GKeyFile to "g_get_user_config_dir()/GLADE_CONFIG_FILENAME"
518
 *
519
 * Returns: 0 on success.
520 521
 */
gint
Tristan Van Berkom's avatar
Tristan Van Berkom committed
522
glade_app_config_save ()
523
{
524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 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 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626
  GIOChannel *channel;
  GIOStatus status;
  gchar *data = NULL, *filename;
  const gchar *config_dir = g_get_user_config_dir ();
  GError *error = NULL;
  gsize size, written, bytes_written = 0;
  static gboolean error_shown = FALSE;
  GladeApp *app;

  /* If we had any errors; wait untill next session to retry.
   */
  if (error_shown)
    return -1;

  app = glade_app_get ();

  /* Just in case... try to create the config directory */
  if (g_file_test (config_dir, G_FILE_TEST_IS_DIR) == FALSE)
    {
      if (g_file_test (config_dir, G_FILE_TEST_EXISTS))
        {
          /* Config dir exists but is not a directory */
          glade_util_ui_message
              (glade_app_get_window (),
               GLADE_UI_ERROR, NULL,
               _("Trying to save private data to %s directory "
                 "but it is a regular file.\n"
                 "No private data will be saved in this session"), config_dir);
          error_shown = TRUE;
          return -1;
        }
      else if (g_mkdir (config_dir, S_IRWXU) != 0)
        {
          /* Doesnt exist; failed to create */
          glade_util_ui_message
              (glade_app_get_window (),
               GLADE_UI_ERROR, NULL,
               _("Failed to create directory %s to save private data.\n"
                 "No private data will be saved in this session"), config_dir);
          error_shown = TRUE;
          return -1;
        }
    }

  filename = g_build_filename (config_dir, GLADE_CONFIG_FILENAME, NULL);

  if ((channel = g_io_channel_new_file (filename, "w", &error)) != NULL)
    {
      if ((data =
           g_key_file_to_data (app->priv->config, &size, &error)) != NULL)
        {

          /* Implement loop here */
          while ((status = g_io_channel_write_chars (channel, data + bytes_written,     /* Offset of write */
                                                     size - bytes_written,      /* Size left to write */
                                                     &written,
                                                     &error)) !=
                 G_IO_STATUS_ERROR && (bytes_written + written) < size)
            bytes_written += written;

          if (status == G_IO_STATUS_ERROR)
            {
              glade_util_ui_message
                  (glade_app_get_window (),
                   GLADE_UI_ERROR, NULL,
                   _("Error writing private data to %s (%s).\n"
                     "No private data will be saved in this session"),
                   filename, error->message);
              error_shown = TRUE;
            }
          g_free (data);
        }
      else
        {
          glade_util_ui_message
              (glade_app_get_window (),
               GLADE_UI_ERROR, NULL,
               _("Error serializing configuration data to save (%s).\n"
                 "No private data will be saved in this session"),
               error->message);
          error_shown = TRUE;
        }
      g_io_channel_shutdown (channel, TRUE, NULL);
      g_io_channel_unref (channel);
    }
  else
    {
      glade_util_ui_message
          (glade_app_get_window (),
           GLADE_UI_ERROR, NULL,
           _("Error opening %s to write private data (%s).\n"
             "No private data will be saved in this session"),
           filename, error->message);
      error_shown = TRUE;
    }
  g_free (filename);

  if (error)
    {
      g_error_free (error);
      return -1;
    }
  return 0;
627 628
}

629 630 631 632 633
/**
 * glade_app_get:
 *
 * Returns: (transfer none): the #GladeApp
 */
634
GladeApp *
Tristan Van Berkom's avatar
Tristan Van Berkom committed
635
glade_app_get (void)
636
{
637
  if (!singleton_app)
638 639 640 641
    {
      singleton_app = glade_app_new ();
    }

642
  return singleton_app;
643 644
}

645 646 647 648 649 650
/**
 * glade_app_set_window:
 * @window: (transfer full): a #GtkWidget
 *
 * Set the window of the application
 */
651
void
652
glade_app_set_window (GtkWidget *window)
653
{
654
  GladeApp *app = glade_app_get ();
Tristan Van Berkom's avatar
Tristan Van Berkom committed
655

656
  app->priv->window = window;
657 658
}

659 660 661 662 663 664
/**
 * glade_app_get_catalog:
 * @name: the name of the catalog
 *
 * Returns: (transfer none) (nullable): a #GladeCatalog or %NULL if none is found
 */
665
GladeCatalog *
666
glade_app_get_catalog (const gchar *name)
667
{
668 669 670
  GladeApp *app = glade_app_get ();
  GList *list;
  GladeCatalog *catalog;
671

672
  g_return_val_if_fail (name && name[0], NULL);
673

674 675 676 677 678 679 680
  for (list = app->priv->catalogs; list; list = list->next)
    {
      catalog = list->data;
      if (!strcmp (glade_catalog_get_name (catalog), name))
        return catalog;
    }
  return NULL;
681 682
}

683 684 685 686 687 688 689 690 691 692
/**
 * glade_app_get_catalog_version:
 * @name: the name of the #GladeCatalog
 * @major: (out) (optional): the major version
 * @minor: (out) (optional): the minor version
 *
 * Returns: %TRUE if the catalog has been found. It is a programming error
 * to call this function with an unexisting catalog, returns %FALSE in this
 * case and throws a warning.
 */
693
gboolean
694
glade_app_get_catalog_version (const gchar *name, gint *major, gint *minor)
695
{
696
  GladeCatalog *catalog = glade_app_get_catalog (name);
697

698
  g_return_val_if_fail (catalog != NULL, FALSE);
699

700 701 702 703
  if (major)
    *major = glade_catalog_get_major_version (catalog);
  if (minor)
    *minor = glade_catalog_get_minor_version (catalog);
704

705
  return TRUE;
706 707
}

708 709 710 711 712
/**
 * glade_app_get_catalogs:
 *
 * Returns: (transfer none) (element-type GladeCatalog): a list of #GladeCatalog
 */
713 714 715
GList *
glade_app_get_catalogs (void)
{
716
  GladeApp *app = glade_app_get ();
717

718
  return app->priv->catalogs;
719 720
}

721 722 723 724 725
/**
 * glade_app_get_window:
 *
 * Returns: (transfer none): a #GtkWidget
 */
Tristan Van Berkom's avatar
Tristan Van Berkom committed
726 727
GtkWidget *
glade_app_get_window (void)
728
{
729 730
  GladeApp *app = glade_app_get ();
  return app->priv->window;
731 732
}

733 734 735 736 737
/**
 * glade_app_get_clipboard:
 *
 * Returns: (transfer none): a #GladeClipboard
 */
Tristan Van Berkom's avatar
Tristan Van Berkom committed
738 739
GladeClipboard *
glade_app_get_clipboard (void)
740
{
741 742
  GladeApp *app = glade_app_get ();
  return app->priv->clipboard;
743
}
744

745
/**
746
 * glade_app_get_projects:
747
 *
748
 * Returns: (element-type GladeCatalog) (transfer none): a list of #GladeCatalog
749
 */
Tristan Van Berkom's avatar
Tristan Van Berkom committed
750 751
GList *
glade_app_get_projects (void)
752
{
753 754
  GladeApp *app = glade_app_get ();
  return app->priv->projects;
755 756
}

757 758 759 760 761
/**
 * glade_app_get_config:
 *
 * Returns: (transfer full): a #GKeyFile
 */
Tristan Van Berkom's avatar
Tristan Van Berkom committed
762 763
GKeyFile *
glade_app_get_config (void)
764
{
Juan Pablo Ugarte's avatar
Juan Pablo Ugarte committed
765 766 767 768 769 770 771 772 773 774 775 776
  static GKeyFile *config = NULL;

  if (config == NULL)
    {
      gchar *filename = g_build_filename (g_get_user_config_dir (),
                                          GLADE_CONFIG_FILENAME, NULL);
      config = g_key_file_new ();
      g_key_file_load_from_file (config, filename, G_KEY_FILE_NONE, NULL);
      g_free (filename);
    }

  return config;
777 778
}

779
gboolean
780
glade_app_is_project_loaded (const gchar *project_path)
781
{
782 783 784
  GladeApp *app;
  GList *list;
  gboolean loaded = FALSE;
785

786 787
  if (project_path == NULL)
    return FALSE;
788

789
  app = glade_app_get ();
Tristan Van Berkom's avatar
Tristan Van Berkom committed
790

791 792 793
  for (list = app->priv->projects; list; list = list->next)
    {
      GladeProject *cur_project = GLADE_PROJECT (list->data);
794

795 796 797 798
      if ((loaded = glade_project_get_path (cur_project) &&
           (strcmp (glade_project_get_path (cur_project), project_path) == 0)))
        break;
    }
799

800
  return loaded;
801 802
}

Tristan Van Berkom's avatar
Tristan Van Berkom committed
803 804 805 806 807 808
/**
 * glade_app_get_project_by_path:
 * @project_path: The path of an open project
 *
 * Finds an open project with @path
 *
809
 * Returns: (nullable) (transfer none): A #GladeProject, or NULL if no such open project was found
Tristan Van Berkom's avatar
Tristan Van Berkom committed
810
 */
811
GladeProject *
812
glade_app_get_project_by_path (const gchar *project_path)
Tristan Van Berkom's avatar
Tristan Van Berkom committed
813
{
814 815 816
  GladeApp *app;
  GList *l;
  gchar *canonical_path;
Tristan Van Berkom's avatar
Tristan Van Berkom committed
817

818 819
  if (project_path == NULL)
    return NULL;
Tristan Van Berkom's avatar
Tristan Van Berkom committed
820

821
  app = glade_app_get ();
Tristan Van Berkom's avatar
Tristan Van Berkom committed
822

823
  canonical_path = glade_util_canonical_path (project_path);
Tristan Van Berkom's avatar
Tristan Van Berkom committed
824

825 826 827
  for (l = app->priv->projects; l; l = l->next)
    {
      GladeProject *project = (GladeProject *) l->data;
Tristan Van Berkom's avatar
Tristan Van Berkom committed
828

829 830 831 832 833 834 835
      if (glade_project_get_path (project) &&
          strcmp (canonical_path, glade_project_get_path (project)) == 0)
        {
          g_free (canonical_path);
          return project;
        }
    }
Tristan Van Berkom's avatar
Tristan Van Berkom committed
836

837
  g_free (canonical_path);
Tristan Van Berkom's avatar
Tristan Van Berkom committed
838

839
  return NULL;
Tristan Van Berkom's avatar
Tristan Van Berkom committed
840
}
841

842 843 844 845
/**
 * glade_app_add_project:
 * @project: the project to add to the #GladeApp
 */
846
void
847
glade_app_add_project (GladeProject *project)
848
{
849
  GladeApp *app;
850

851
  g_return_if_fail (GLADE_IS_PROJECT (project));
852

853
  app = glade_app_get ();
854

855 856
  /* If the project was previously loaded, don't re-load */
  if (g_list_find (app->priv->projects, project) != NULL)
857
    return;
858

859
  /* Take a reference for GladeApp here... */
860
  app->priv->projects = g_list_append (app->priv->projects, g_object_ref (project));
861 862
}

863 864 865 866
/**
 * glade_app_remove_project:
 * @project: the project to remove from the #GladeApp
 */
867
void
868
glade_app_remove_project (GladeProject *project)
869
{
870 871
  GladeApp *app;
  g_return_if_fail (GLADE_IS_PROJECT (project));
872

873
  app = glade_app_get ();
Tristan Van Berkom's avatar
Tristan Van Berkom committed
874

875
  app->priv->projects = g_list_remove (app->priv->projects, project);
Tristan Van Berkom's avatar
Tristan Van Berkom committed
876

877 878 879 880 881
  /* Its safe to just release the project as the project emits a
   * "close" signal and everyone is responsable for cleaning up at
   * that point.
   */
  g_object_unref (project);
882 883
}

884
/**
885
 * glade_app_set_accel_group:
886
 * @accel_group: (transfer full): a #GtkAccelGroup to set
887
 *
Vincent Geddes's avatar
Vincent Geddes committed
888
 * Sets @accel_group to app.
889 890
 * The acceleration group will made available for editor dialog windows
 * from the plugin backend.
891 892
 */
void
893
glade_app_set_accel_group (GtkAccelGroup *accel_group)
894
{
895 896
  GladeApp *app;
  g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
Tristan Van Berkom's avatar
Tristan Van Berkom committed
897

898 899 900
  app = glade_app_get ();

  app->priv->accel_group = accel_group;
901 902
}

903 904 905 906 907
/**
 * glade_app_get_accel_group:
 *
 * Returns: (transfer none): the #GtkAccelGroup
 */
908 909 910
GtkAccelGroup *
glade_app_get_accel_group (void)
{
911
  return glade_app_get ()->priv->accel_group;
912 913
}

914 915 916 917 918
/**
 * glade_app_new:
 *
 * Returns: (transfer full): the #GladeApp
 */
919
GladeApp *
920
glade_app_new (void)
921
{
922
  return g_object_new (GLADE_TYPE_APP, NULL);
923 924
}

925 926 927 928 929 930 931 932
/**
 * glade_app_search_docs:
 * @book: the name of a book
 * @page: the name of a page
 * @search: the search query
 *
 * Searches for @book, @page and @search in the documentation.
 */
933
void
934
glade_app_search_docs (const gchar *book,
935 936
                       const gchar *page, 
                       const gchar *search)
937
{
938
  GladeApp *app;
939

940
  app = glade_app_get ();
941

942
  g_signal_emit (G_OBJECT (app), glade_app_signals[DOC_SEARCH], 0, 
943
                 book, page, search);
944
}