1 /* 2 * Copyright (C) 2001 Ximian, Inc. 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as 6 * published by the Free Software Foundation; either version 2 of the 7 * License, or (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * 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 * Authors: 19 * Naba Kumar <naba@gnome.org> 20 */ 21 22 #include <config.h> 23 24 /** 25 * SECTION:glade-app 26 * @Short_Description: The central control point of the Glade core. 27 * 28 * This main control object is where we try to draw the line between 29 * what is the Glade core and what is the main application. The main 30 * application must derive from the GladeApp object and create an instance 31 * to initialize the Glade core. 32 */ 33 34 #include "glade.h" 35 #include "glade-debug.h" 36 #include "glade-cursor.h" 37 #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 #include <string.h> 44 #include <glib.h> 45 #include <glib/gstdio.h> 46 #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 #endif 53 54 #define GLADE_CONFIG_FILENAME "glade.conf" 55 56 enum 57 { 58 DOC_SEARCH, 59 SIGNAL_EDITOR_CREATED, 60 WIDGET_ADAPTOR_REGISTERED, 61 LAST_SIGNAL 62 }; 63 64 struct _GladeAppPrivate 65 { 66 GtkWidget *window; 67 68 GladeClipboard *clipboard; /* See glade-clipboard */ 69 GList *catalogs; /* See glade-catalog */ 70 71 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 static guint glade_app_signals[LAST_SIGNAL] = { 0 }; 79 80 /* installation paths */ 81 static gchar *catalogs_dir = NULL; 82 static gchar *modules_dir = NULL; 83 static gchar *pixmaps_dir = NULL; 84 static gchar *locale_dir = NULL; 85 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); 92 93 /***************************************************************** 94 * GObjectClass * 95 *****************************************************************/ 96 static GObject * 97 glade_app_constructor (GType type, 98 guint n_construct_properties, 99 GObjectConstructParam *construct_properties) 100 { 101 GObject *object; 102 103 /* singleton */ 104 if (!singleton_app) 105 { 106 object = G_OBJECT_CLASS (glade_app_parent_class)->constructor (type, 107 n_construct_properties, 108 construct_properties); 109 singleton_app = GLADE_APP (object); 110 } 111 else 112 { 113 g_object_ref (singleton_app); 114 } 115 116 return G_OBJECT (singleton_app); 117 } 118 119 120 121 static void 122 glade_app_dispose (GObject *app) 123 { 124 GladeAppPrivate *priv = GLADE_APP (app)->priv; 125 126 if (priv->clipboard) 127 { 128 g_object_unref (priv->clipboard); 129 priv->clipboard = NULL; 130 } 131 /* FIXME: Remove projects */ 132 133 if (priv->config) 134 { 135 g_key_file_free (priv->config); 136 priv->config = NULL; 137 } 138 139 G_OBJECT_CLASS (glade_app_parent_class)->dispose (app); 140 } 141 142 static void 143 glade_app_finalize (GObject *app) 144 { 145 g_free (catalogs_dir); 146 g_free (modules_dir); 147 g_free (pixmaps_dir); 148 g_free (locale_dir); 149 g_free (bin_dir); 150 g_free (lib_dir); 151 152 singleton_app = NULL; 153 check_initialised = FALSE; 154 155 G_OBJECT_CLASS (glade_app_parent_class)->finalize (app); 156 } 157 158 /* build package paths at runtime */ 159 static void 160 build_package_paths (void) 161 { 162 const gchar *path; 163 164 path = g_getenv (GLADE_ENV_PIXMAP_DIR); 165 if (path) 166 pixmaps_dir = g_strdup (path); 167 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 if (!pixmaps_dir) 180 pixmaps_dir = g_build_filename (prefix, "share", PACKAGE, "pixmaps", NULL); 181 182 catalogs_dir = g_build_filename (prefix, "share", PACKAGE, "catalogs", NULL); 183 modules_dir = g_build_filename (prefix, "lib", PACKAGE, "modules", NULL); 184 locale_dir = g_build_filename (prefix, "share", "locale", NULL); 185 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 catalogs_dir = g_strdup (GLADE_CATALOGSDIR); 191 modules_dir = g_strdup (GLADE_MODULESDIR); 192 193 if (!pixmaps_dir) 194 pixmaps_dir = g_strdup (GLADE_PIXMAPSDIR); 195 locale_dir = g_strdup (GLADE_LOCALEDIR); 196 bin_dir = g_strdup (GLADE_BINDIR); 197 lib_dir = g_strdup (GLADE_LIBDIR); 198 #endif 199 } 200 201 /* initialization function for libgladeui (not GladeApp) */ 202 static void 203 glade_init_check (void) 204 { 205 if (check_initialised) 206 return; 207 208 glade_init_debug_flags (); 209 210 /* Make sure path accessors work on osx */ 211 build_package_paths (); 212 213 bindtextdomain (GETTEXT_PACKAGE, locale_dir); 214 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); 215 216 check_initialised = TRUE; 217 } 218 219 /***************************************************************** 220 * GladeAppClass * 221 *****************************************************************/ 222 const gchar * 223 glade_app_get_catalogs_dir (void) 224 { 225 glade_init_check (); 226 227 return catalogs_dir; 228 } 229 230 const gchar * 231 glade_app_get_modules_dir (void) 232 { 233 glade_init_check (); 234 235 return modules_dir; 236 } 237 238 const gchar * 239 glade_app_get_pixmaps_dir (void) 240 { 241 glade_init_check (); 242 243 return pixmaps_dir; 244 } 245 246 const gchar * 247 glade_app_get_locale_dir (void) 248 { 249 glade_init_check (); 250 251 return locale_dir; 252 } 253 254 const gchar * 255 glade_app_get_bin_dir (void) 256 { 257 glade_init_check (); 258 259 return bin_dir; 260 } 261 262 const gchar * 263 glade_app_get_lib_dir (void) 264 { 265 glade_init_check (); 266 267 return lib_dir; 268 } 269 270 static void 271 pointer_mode_register_icon (const gchar *icon_name, 272 gint real_size, 273 GladePointerMode mode, 274 GtkIconSize size) 275 { 276 GdkPixbuf *pixbuf; 277 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 g_object_unref (pixbuf); 282 } 283 } 284 285 static void 286 register_icon (const gchar *new_icon_name, 287 gint size, 288 const gchar *icon_name, 289 const gchar *file_name) 290 { 291 GtkIconTheme *icon_theme = gtk_icon_theme_get_default (); 292 GdkPixbuf *pixbuf; 293 GtkIconInfo *info; 294 295 if ((info = gtk_icon_theme_lookup_icon (icon_theme, icon_name, size, 0))) 296 { 297 pixbuf = gtk_icon_info_load_icon (info, NULL); 298 } 299 else 300 { 301 gchar *path = g_build_filename (glade_app_get_pixmaps_dir (), file_name, NULL); 302 pixbuf = gdk_pixbuf_new_from_file (path, NULL); 303 g_free (path); 304 } 305 306 if (pixbuf) 307 { 308 gtk_icon_theme_add_builtin_icon (new_icon_name, size, pixbuf); 309 g_object_unref (pixbuf); 310 } 311 } 312 313 /* 314 * glade_app_register_icon_names: 315 * @size: icon size 316 * 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 if (gtk_icon_size_lookup (size, &w, &h) == FALSE) 327 return; 328 329 real_size = MIN (w, h); 330 331 pointer_mode_register_icon ("glade-selector", real_size, GLADE_POINTER_SELECT, size); 332 pointer_mode_register_icon ("glade-drag-resize", real_size, GLADE_POINTER_DRAG_RESIZE, size); 333 pointer_mode_register_icon ("glade-margin-edit", real_size, GLADE_POINTER_MARGIN_EDIT, size); 334 pointer_mode_register_icon ("glade-align-edit", real_size, GLADE_POINTER_ALIGN_EDIT, size); 335 336 register_icon ("glade-devhelp", real_size, 337 GLADE_DEVHELP_ICON_NAME, 338 GLADE_DEVHELP_FALLBACK_ICON_FILE); 339 } 340 341 /** 342 * glade_init: 343 * 344 * Initialization function for libgladeui (not #GladeApp) 345 * It builds paths, bind text domain, and register icons 346 */ 347 void 348 glade_init (void) 349 { 350 static gboolean init = FALSE; 351 352 if (init) return; 353 354 glade_init_check (); 355 356 /* Register icons needed by the UI */ 357 glade_app_register_icon_names (GTK_ICON_SIZE_LARGE_TOOLBAR); 358 359 init = TRUE; 360 } 361 362 static void 363 glade_app_init (GladeApp *app) 364 { 365 static gboolean initialized = FALSE; 366 GladeAppPrivate *priv = app->priv = glade_app_get_instance_private (app); 367 368 singleton_app = app; 369 370 glade_init (); 371 372 if (!initialized) 373 { 374 GtkIconTheme *default_icon_theme = gtk_icon_theme_get_default (); 375 const gchar *path; 376 377 gtk_icon_theme_append_search_path (default_icon_theme, pixmaps_dir); 378 379 /* Handle extra icon theme paths. Needed for tests to work */ 380 if ((path = g_getenv (GLADE_ENV_ICON_THEME_PATH))) 381 { 382 gchar **tokens = g_strsplit (path, ":", -1); 383 gint i; 384 385 for (i = 0; tokens[i]; i++) 386 gtk_icon_theme_append_search_path (default_icon_theme, tokens[i]); 387 388 g_strfreev (tokens); 389 } 390 391 glade_cursor_init (); 392 393 initialized = TRUE; 394 } 395 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 */ 405 priv->config = g_key_file_ref (glade_app_get_config ()); 406 } 407 408 static void 409 glade_app_event_handler (GdkEvent *event, gpointer data) 410 { 411 if (glade_app_do_event (event)) return; 412 413 gtk_main_do_event (event); 414 } 415 416 static void 417 glade_app_class_init (GladeAppClass *klass) 418 { 419 GObjectClass *object_class; 420 421 object_class = G_OBJECT_CLASS (klass); 422 423 object_class->constructor = glade_app_constructor; 424 object_class->dispose = glade_app_dispose; 425 object_class->finalize = glade_app_finalize; 426 427 /** 428 * GladeApp::doc-search: 429 * @gladeeditor: the #GladeEditor which received the signal. 430 * @arg1: the (#gchar *) book to search or %NULL 431 * @arg2: the (#gchar *) page to search or %NULL 432 * @arg3: the (#gchar *) search string or %NULL 433 * 434 * Emitted when the glade core requests that a doc-search be performed. 435 */ 436 glade_app_signals[DOC_SEARCH] = 437 g_signal_new ("doc-search", 438 G_TYPE_FROM_CLASS (object_class), 439 G_SIGNAL_RUN_LAST, 0, NULL, NULL, 440 _glade_marshal_VOID__STRING_STRING_STRING, 441 G_TYPE_NONE, 3, 442 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); 443 444 /** 445 * GladeApp::signal-editor-created: 446 * @gladeapp: the #GladeApp which received the signal. 447 * @signal_editor: the new #GladeSignalEditor. 448 * 449 * Emitted when a new signal editor created. 450 * A tree view is created in the default handler. 451 * Connect your handler before the default handler for setting a custom column or renderer 452 * and after it for connecting to the tree view signals 453 */ 454 glade_app_signals[SIGNAL_EDITOR_CREATED] = 455 g_signal_new ("signal-editor-created", 456 G_TYPE_FROM_CLASS (object_class), 457 G_SIGNAL_RUN_LAST, 458 0, NULL, NULL, 459 _glade_marshal_VOID__OBJECT, 460 G_TYPE_NONE, 1, G_TYPE_OBJECT); 461 462 /** 463 * GladeApp::widget-adaptor-registered: 464 * @gladeapp: the #GladeApp which received the signal. 465 * @adaptor: the newlly registered #GladeWidgetAdaptor. 466 * 467 * Emitted when a new widget adaptor is registered. 468 */ 469 glade_app_signals[WIDGET_ADAPTOR_REGISTERED] = 470 g_signal_new ("widget-adaptor-registered", 471 G_TYPE_FROM_CLASS (object_class), 472 G_SIGNAL_RUN_LAST, 473 0, NULL, NULL, 474 _glade_marshal_VOID__OBJECT, 475 G_TYPE_NONE, 1, G_TYPE_OBJECT); 476 477 gdk_event_handler_set (glade_app_event_handler, NULL, NULL); 478 } 479 480 /***************************************************************** 481 * Public API * 482 *****************************************************************/ 483 484 /** 485 * glade_app_do_event: 486 * @event: the event to process. 487 * 488 * This function has to be called in an event handler for widget selection to work. 489 * See gdk_event_handler_set() 490 * 491 * Returns: true if the event was handled. 492 */ 493 gboolean 494 glade_app_do_event (GdkEvent *event) 495 { 496 GdkWindow *window = event->any.window; 497 GtkWidget *layout; 498 gpointer widget; 499 500 if (window == NULL) return FALSE; 501 502 gdk_window_get_user_data (window, &widget); 503 504 /* As a slight optimization we could replace gtk_widget_get_ancestor() 505 * with a custom function that only iterates trought parents with windows. 506 */ 507 if (widget && IS_GLADE_WIDGET_EVENT (event->type) && 508 (layout = gtk_widget_get_ancestor (widget, GLADE_TYPE_DESIGN_LAYOUT))) 509 return _glade_design_layout_do_event (GLADE_DESIGN_LAYOUT (layout), event); 510 511 return FALSE; 512 } 513 514 /** 515 * glade_app_config_save 516 * 517 * Saves the GKeyFile to "g_get_user_config_dir()/GLADE_CONFIG_FILENAME" 518 * 519 * Return 0 on success. 520 */ 521 gint 522 glade_app_config_save () 523 { 524 GIOChannel *channel; 525 GIOStatus status; 526 gchar *data = NULL, *filename; 527 const gchar *config_dir = g_get_user_config_dir (); 528 GError *error = NULL; 529 gsize size, written, bytes_written = 0; 530 static gboolean error_shown = FALSE; 531 GladeApp *app; 532 533 /* If we had any errors; wait untill next session to retry. 534 */ 535 if (error_shown) 536 return -1; 537 538 app = glade_app_get (); 539 540 /* Just in case... try to create the config directory */ 541 if (g_file_test (config_dir, G_FILE_TEST_IS_DIR) == FALSE) 542 { 543 if (g_file_test (config_dir, G_FILE_TEST_EXISTS)) 544 { 545 /* Config dir exists but is not a directory */ 546 glade_util_ui_message 547 (glade_app_get_window (), 548 GLADE_UI_ERROR, NULL, 549 _("Trying to save private data to %s directory " 550 "but it is a regular file.\n" 551 "No private data will be saved in this session"), config_dir); 552 error_shown = TRUE; 553 return -1; 554 } 555 else if (g_mkdir (config_dir, S_IRWXU) != 0) 556 { 557 /* Doesnt exist; failed to create */ 558 glade_util_ui_message 559 (glade_app_get_window (), 560 GLADE_UI_ERROR, NULL, 561 _("Failed to create directory %s to save private data.\n" 562 "No private data will be saved in this session"), config_dir); 563 error_shown = TRUE; 564 return -1; 565 } 566 } 567 568 filename = g_build_filename (config_dir, GLADE_CONFIG_FILENAME, NULL); 569 570 if ((channel = g_io_channel_new_file (filename, "w", &error)) != NULL) 571 { 572 if ((data = 573 g_key_file_to_data (app->priv->config, &size, &error)) != NULL) 574 { 575 576 /* Implement loop here */ 577 while ((status = g_io_channel_write_chars (channel, data + bytes_written, /* Offset of write */ 578 size - bytes_written, /* Size left to write */ 579 &written, 580 &error)) != 581 G_IO_STATUS_ERROR && (bytes_written + written) < size) 582 bytes_written += written; 583 584 if (status == G_IO_STATUS_ERROR) 585 { 586 glade_util_ui_message 587 (glade_app_get_window (), 588 GLADE_UI_ERROR, NULL, 589 _("Error writing private data to %s (%s).\n" 590 "No private data will be saved in this session"), 591 filename, error->message); 592 error_shown = TRUE; 593 } 594 g_free (data); 595 } 596 else 597 { 598 glade_util_ui_message 599 (glade_app_get_window (), 600 GLADE_UI_ERROR, NULL, 601 _("Error serializing configuration data to save (%s).\n" 602 "No private data will be saved in this session"), 603 error->message); 604 error_shown = TRUE; 605 } 606 g_io_channel_shutdown (channel, TRUE, NULL); 607 g_io_channel_unref (channel); 608 } 609 else 610 { 611 glade_util_ui_message 612 (glade_app_get_window (), 613 GLADE_UI_ERROR, NULL, 614 _("Error opening %s to write private data (%s).\n" 615 "No private data will be saved in this session"), 616 filename, error->message); 617 error_shown = TRUE; 618 } 619 g_free (filename); 620 621 if (error) 622 { 623 g_error_free (error); 624 return -1; 625 } 626 return 0; 627 } 628 629 GladeApp * 630 glade_app_get (void) 631 { 632 if (!singleton_app) 633 { 634 singleton_app = glade_app_new (); 635 } 636 637 return singleton_app; 638 } 639 640 void 641 glade_app_set_window (GtkWidget *window) 642 { 643 GladeApp *app = glade_app_get (); 644 645 app->priv->window = window; 646 } 647 648 GladeCatalog * 649 glade_app_get_catalog (const gchar *name) 650 { 651 GladeApp *app = glade_app_get (); 652 GList *list; 653 GladeCatalog *catalog; 654 655 g_return_val_if_fail (name && name[0], NULL); 656 657 for (list = app->priv->catalogs; list; list = list->next) 658 { 659 catalog = list->data; 660 if (!strcmp (glade_catalog_get_name (catalog), name)) 661 return catalog; 662 } 663 return NULL; 664 } 665 666 gboolean 667 glade_app_get_catalog_version (const gchar *name, gint *major, gint *minor) 668 { 669 GladeCatalog *catalog = glade_app_get_catalog (name); 670 671 g_return_val_if_fail (catalog != NULL, FALSE); 672 673 if (major) 674 *major = glade_catalog_get_major_version (catalog); 675 if (minor) 676 *minor = glade_catalog_get_minor_version (catalog); 677 678 return TRUE; 679 } 680 681 GList * 682 glade_app_get_catalogs (void) 683 { 684 GladeApp *app = glade_app_get (); 685 686 return app->priv->catalogs; 687 } 688 689 690 GtkWidget * 691 glade_app_get_window (void) 692 { 693 GladeApp *app = glade_app_get (); 694 return app->priv->window; 695 } 696 697 GladeClipboard * 698 glade_app_get_clipboard (void) 699 { 700 GladeApp *app = glade_app_get (); 701 return app->priv->clipboard; 702 } 703 /** 704 * glade_app_get_catalogs: 705 * 706 * Return value: (element-type GladeCatalog): catalogs 707 */ 708 GList * 709 glade_app_get_projects (void) 710 { 711 GladeApp *app = glade_app_get (); 712 return app->priv->projects; 713 } 714 715 GKeyFile * 716 glade_app_get_config (void) 717 { 718 static GKeyFile *config = NULL; 719 720 if (config == NULL) 721 { 722 gchar *filename = g_build_filename (g_get_user_config_dir (), 723 GLADE_CONFIG_FILENAME, NULL); 724 config = g_key_file_new (); 725 g_key_file_load_from_file (config, filename, G_KEY_FILE_NONE, NULL); 726 g_free (filename); 727 } 728 729 return config; 730 } 731 732 gboolean 733 glade_app_is_project_loaded (const gchar *project_path) 734 { 735 GladeApp *app; 736 GList *list; 737 gboolean loaded = FALSE; 738 739 if (project_path == NULL) 740 return FALSE; 741 742 app = glade_app_get (); 743 744 for (list = app->priv->projects; list; list = list->next) 745 { 746 GladeProject *cur_project = GLADE_PROJECT (list->data); 747 748 if ((loaded = glade_project_get_path (cur_project) && 749 (strcmp (glade_project_get_path (cur_project), project_path) == 0))) 750 break; 751 } 752 753 return loaded; 754 } 755 756 /** 757 * glade_app_get_project_by_path: 758 * @project_path: The path of an open project 759 * 760 * Finds an open project with @path 761 * 762 * Returns: A #GladeProject, or NULL if no such open project was found 763 */ 764 GladeProject * 765 glade_app_get_project_by_path (const gchar *project_path) 766 { 767 GladeApp *app; 768 GList *l; 769 gchar *canonical_path; 770 771 if (project_path == NULL) 772 return NULL; 773 774 app = glade_app_get (); 775 776 canonical_path = glade_util_canonical_path (project_path); 777 778 for (l = app->priv->projects; l; l = l->next) 779 { 780 GladeProject *project = (GladeProject *) l->data; 781 782 if (glade_project_get_path (project) && 783 strcmp (canonical_path, glade_project_get_path (project)) == 0) 784 { 785 g_free (canonical_path); 786 return project; 787 } 788 } 789 790 g_free (canonical_path); 791 792 return NULL; 793 } 794 795 void 796 glade_app_add_project (GladeProject *project) 797 { 798 GladeApp *app; 799 800 g_return_if_fail (GLADE_IS_PROJECT (project)); 801 802 app = glade_app_get (); 803 804 /* If the project was previously loaded, don't re-load */ 805 if (g_list_find (app->priv->projects, project) != NULL) 806 return; 807 808 /* Take a reference for GladeApp here... */ 809 app->priv->projects = g_list_append (app->priv->projects, g_object_ref (project)); 810 } 811 812 void 813 glade_app_remove_project (GladeProject *project) 814 { 815 GladeApp *app; 816 g_return_if_fail (GLADE_IS_PROJECT (project)); 817 818 app = glade_app_get (); 819 820 app->priv->projects = g_list_remove (app->priv->projects, project); 821 822 /* Its safe to just release the project as the project emits a 823 * "close" signal and everyone is responsable for cleaning up at 824 * that point. 825 */ 826 g_object_unref (project); 827 } 828 829 /* 830 * glade_app_set_accel_group: 831 * 832 * Sets @accel_group to app. 833 * The acceleration group will made available for editor dialog windows 834 * from the plugin backend. 835 */ 836 void 837 glade_app_set_accel_group (GtkAccelGroup *accel_group) 838 { 839 GladeApp *app; 840 g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group)); 841 842 app = glade_app_get (); 843 844 app->priv->accel_group = accel_group; 845 } 846 847 GtkAccelGroup * 848 glade_app_get_accel_group (void) 849 { 850 return glade_app_get ()->priv->accel_group; 851 } 852 853 GladeApp * 854 glade_app_new (void) 855 { 856 return g_object_new (GLADE_TYPE_APP, NULL); 857 } 858 859 void 860 glade_app_search_docs (const gchar *book, 861 const gchar *page, 862 const gchar *search) 863 { 864 GladeApp *app; 865 866 app = glade_app_get (); 867 868 g_signal_emit (G_OBJECT (app), glade_app_signals[DOC_SEARCH], 0, 869 book, page, search); 870 } 871