You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
444 lines
12 KiB
444 lines
12 KiB
10 years ago
|
/*
|
||
|
*
|
||
|
* Copyright (c) 2008-2009 Erich Hoover
|
||
|
*
|
||
|
* libr GTK support - Convenience functions for using resources in GTK applications
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||
|
* the Free Software Foundation; either version 2.1 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program; if not, write to the Free Software
|
||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||
|
*
|
||
|
* To provide feedback, report bugs, or otherwise contact me:
|
||
|
* ehoover at mines dot edu
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
/* Include compile-time parameters */
|
||
|
#include "config.h"
|
||
|
|
||
|
#include "libr.h"
|
||
|
#include "libr-gtk.h"
|
||
|
#include "libr-icons.h"
|
||
|
#include "tempfiles.h"
|
||
|
|
||
|
/* For loading GTK/GDK images */
|
||
|
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||
|
#include <glib.h>
|
||
|
|
||
|
/* For loading GLADE files */
|
||
|
#include <glade/glade.h>
|
||
|
|
||
|
/* For loading GTK+ Builder files */
|
||
|
#include <gtk/gtk.h>
|
||
|
|
||
|
/* For malloc/free */
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
/* For string handling */
|
||
|
#include <string.h>
|
||
|
|
||
|
typedef gchar * (*GladeFileCallback)(GladeXML *, const gchar *, guint *);
|
||
|
GladeFileCallback glade_set_file_callback(GladeFileCallback callback, gpointer user_data);
|
||
|
|
||
|
/* Use weak binding for all glade and GTK+ requirements */
|
||
|
#pragma weak glade_set_file_callback
|
||
|
|
||
|
#pragma weak gtk_window_set_default_icon_list
|
||
|
#pragma weak gdk_pixbuf_loader_get_pixbuf
|
||
|
#pragma weak gtk_builder_add_from_string
|
||
|
#pragma weak gdk_pixbuf_loader_set_size
|
||
|
#pragma weak g_type_check_instance_cast
|
||
|
#pragma weak gtk_builder_add_from_file
|
||
|
#pragma weak glade_xml_new_from_buffer
|
||
|
#pragma weak gdk_pixbuf_loader_write
|
||
|
#pragma weak gdk_pixbuf_loader_close
|
||
|
#pragma weak gdk_pixbuf_loader_new
|
||
|
#pragma weak g_signal_connect_data
|
||
|
#pragma weak g_signal_connect
|
||
|
#pragma weak gtk_builder_new
|
||
|
#pragma weak g_object_unref
|
||
|
#pragma weak glade_xml_new
|
||
|
#pragma weak g_list_append
|
||
|
#pragma weak glade_init
|
||
|
#pragma weak gtk_init
|
||
|
#pragma weak g_free
|
||
|
|
||
|
#define GLADE_SECTION ".glade"
|
||
|
#define BUILDER_SECTION ".ui"
|
||
|
|
||
|
/*
|
||
|
* Handle the resource request from libglade
|
||
|
*/
|
||
|
gchar *libr_glade_read_resource(GladeXML *gladefile, const gchar *filename, guint *size, gpointer user_data)
|
||
|
{
|
||
|
return libr_malloc((libr_file *) user_data, (char *) filename, (size_t *) size);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Handle the resource request from GtkBuilder
|
||
|
*/
|
||
|
gboolean libr_gtk_read_resource(GtkBuilder *builder, const gchar *filename, gchar **data, gsize *size, GError **error, gpointer user_data)
|
||
|
{
|
||
|
if(data == NULL)
|
||
|
return FALSE;
|
||
|
*data = libr_malloc((libr_file *) user_data, (char *) filename, (size_t *) size);
|
||
|
if(*data == NULL)
|
||
|
return FALSE;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Load the libglade resource appropriately for the currently installed version
|
||
|
* (AKA, hurray hacks!)
|
||
|
*/
|
||
|
GladeXML *libr_new_glade(libr_file *handle, char *gladefile, size_t gladefile_size)
|
||
|
{
|
||
|
if(glade_set_file_callback) /* The not-yet (ever?) existing way */
|
||
|
{
|
||
|
/* Register a callback for libglade to load our resources */
|
||
|
if(glade_set_file_callback((GladeFileCallback) libr_glade_read_resource, handle) != NULL)
|
||
|
printf("warning: over-wrote an application callback!\n");
|
||
|
/* Initialize libglade almost as usual, just use a buffer instead of a file */
|
||
|
return glade_xml_new_from_buffer(gladefile, gladefile_size, NULL, NULL);
|
||
|
}
|
||
|
else /* The hacky way */
|
||
|
{
|
||
|
char *glade_file[PATH_MAX];
|
||
|
GladeXML *ret = NULL;
|
||
|
char *temp_folder;
|
||
|
|
||
|
temp_folder = libr_extract_resources(handle);
|
||
|
if(temp_folder == NULL)
|
||
|
return NULL;
|
||
|
strcpy((char*)glade_file, temp_folder);
|
||
|
strcat((char*)glade_file, "/");
|
||
|
strcat((char*)glade_file, GLADE_SECTION);
|
||
|
ret = glade_xml_new((char*)glade_file, NULL, NULL);
|
||
|
if(ret == NULL)
|
||
|
cleanup_folder(temp_folder);
|
||
|
else
|
||
|
register_folder_cleanup(temp_folder);
|
||
|
return ret;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Load the GtkBuilder resource appropriately for the currently installed version
|
||
|
* (AKA, hurray hacks!)
|
||
|
*/
|
||
|
int libr_new_builder(libr_file *handle, char *builderfile, size_t builderfile_size, GtkBuilder *builder)
|
||
|
{
|
||
|
/* Register a callback for GtkBuilder to load our resources */
|
||
|
if(g_signal_connect(builder, "load-resource", (GCallback) libr_gtk_read_resource, handle))
|
||
|
{
|
||
|
/* Initialize GtkBuilder almost as usual, just use a buffer instead of a file */
|
||
|
if(gtk_builder_add_from_string(builder, builderfile, builderfile_size, NULL) == 0)
|
||
|
return false;
|
||
|
return true;
|
||
|
}
|
||
|
else /* The hacky way */
|
||
|
{
|
||
|
char *builder_file[PATH_MAX];
|
||
|
char *temp_folder;
|
||
|
int ret = false;
|
||
|
|
||
|
temp_folder = libr_extract_resources(handle);
|
||
|
if(temp_folder == NULL)
|
||
|
return false;
|
||
|
strcpy((char*)builder_file, temp_folder);
|
||
|
strcat((char*)builder_file, "/");
|
||
|
strcat((char*)builder_file, BUILDER_SECTION);
|
||
|
ret = gtk_builder_add_from_file(builder, (char*)builder_file, NULL);
|
||
|
if(ret == 0)
|
||
|
cleanup_folder(temp_folder);
|
||
|
else
|
||
|
register_folder_cleanup(temp_folder);
|
||
|
g_free(temp_folder);
|
||
|
return (ret != 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Return a GTK icon list
|
||
|
*/
|
||
|
EXPORT_FN IconList *libr_gtk_iconlist(libr_file *handle)
|
||
|
{
|
||
|
int sizes[] = {16, 32, 48, 96, 128};
|
||
|
IconList *icons = NULL;
|
||
|
GdkPixbuf *icon = NULL;
|
||
|
int sizes_len = 5, i;
|
||
|
|
||
|
if(handle == NULL)
|
||
|
{
|
||
|
/* Must pass a file handle to obtain the icons from */
|
||
|
return NULL;
|
||
|
}
|
||
|
if(gtk_init == NULL)
|
||
|
{
|
||
|
/* GTK+ was not linked with the application */
|
||
|
return false;
|
||
|
}
|
||
|
/* Go through the list of GTK "required" image sizes and build the icons */
|
||
|
for(i=0;i<sizes_len;i++)
|
||
|
{
|
||
|
libr_icon *icon_handle = libr_icon_geticon_bysize(handle, sizes[i]);
|
||
|
GdkPixbufLoader *stream = gdk_pixbuf_loader_new();
|
||
|
char *iconfile = NULL;
|
||
|
size_t iconfile_size;
|
||
|
|
||
|
if(icon_handle == NULL)
|
||
|
{
|
||
|
/* Failed to find an icon */
|
||
|
printf("Failed to find an icon\n");
|
||
|
continue;
|
||
|
}
|
||
|
iconfile = libr_icon_malloc(icon_handle, &iconfile_size);
|
||
|
if(iconfile == NULL)
|
||
|
{
|
||
|
/* Failed to obtain embedded icon */
|
||
|
continue;
|
||
|
}
|
||
|
libr_icon_close(icon_handle);
|
||
|
/* TODO: Use the "size-prepared" signal to properly scale the width and height
|
||
|
void user_function (GdkPixbufLoader *loader, gint width, gint height, gpointer user_data)
|
||
|
*/
|
||
|
gdk_pixbuf_loader_set_size(stream, sizes[i], sizes[i]);
|
||
|
if(!gdk_pixbuf_loader_write(stream, (unsigned char *)iconfile, iconfile_size, NULL))
|
||
|
{
|
||
|
/* Failed to process image */
|
||
|
continue;
|
||
|
}
|
||
|
if(!gdk_pixbuf_loader_close(stream, NULL))
|
||
|
{
|
||
|
/* Failed to create image */
|
||
|
continue;
|
||
|
}
|
||
|
icon = gdk_pixbuf_loader_get_pixbuf(stream);
|
||
|
if(icon == NULL)
|
||
|
{
|
||
|
/* Failed to convert image to pixbuf */
|
||
|
continue;
|
||
|
}
|
||
|
icons = g_list_append(icons, icon);
|
||
|
}
|
||
|
return icons;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Shared GtkBuilder resource loading
|
||
|
*/
|
||
|
GtkBuilder *libr_gtk_load_internal(libr_file *handle, char *resource_name)
|
||
|
{
|
||
|
GtkBuilder *builder = NULL;
|
||
|
size_t builder_length;
|
||
|
char *builder_data;
|
||
|
|
||
|
/* Obtain the GtkBuilder XML definition */
|
||
|
builder_data = libr_malloc(handle, resource_name, &builder_length);
|
||
|
if(builder_data == NULL)
|
||
|
{
|
||
|
/* Failed to obtain embedded GtkBuilder definition file */
|
||
|
goto failed;
|
||
|
}
|
||
|
/* Setup the GtkBuilder environment */
|
||
|
builder = gtk_builder_new();
|
||
|
if(builder == NULL)
|
||
|
goto failed;
|
||
|
if(!libr_new_builder(handle, builder_data, builder_length, builder))
|
||
|
{
|
||
|
/* Failed to build interface from resource file */
|
||
|
g_object_unref(G_OBJECT(builder));
|
||
|
goto failed;
|
||
|
}
|
||
|
failed:
|
||
|
free(builder_data);
|
||
|
return builder;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Load the requested GtkBuilder resource and any applicable icons
|
||
|
*/
|
||
|
EXPORT_FN int libr_gtk_load(BuilderHandle **gtk_ret, char *resource_name)
|
||
|
{
|
||
|
libr_file *handle;
|
||
|
int ret = false;
|
||
|
|
||
|
if(gtk_ret == NULL)
|
||
|
{
|
||
|
/* Why on earth would you call this without obtaining the handle to the resource? */
|
||
|
return false;
|
||
|
}
|
||
|
if(gtk_builder_new == NULL)
|
||
|
{
|
||
|
/* GtkBuilder was not linked with the application */
|
||
|
return false;
|
||
|
}
|
||
|
/* Obtain the handle to the executable */
|
||
|
if((handle = libr_open(NULL, LIBR_READ)) == NULL)
|
||
|
{
|
||
|
/* "Failed to open this executable (%s) for resources", progname() */
|
||
|
return false;
|
||
|
}
|
||
|
register_internal_handle(handle);
|
||
|
*gtk_ret = libr_gtk_load_internal(handle, resource_name);
|
||
|
if(*gtk_ret == NULL)
|
||
|
goto failed;
|
||
|
ret = true;
|
||
|
failed:
|
||
|
if(!ret)
|
||
|
libr_close(handle);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Automatically load the ".ui" GtkBuilder resource and any applicable icons
|
||
|
*/
|
||
|
EXPORT_FN int libr_gtk_autoload(BuilderHandle **gtk_ret, IconList **icons_ret, int set_default_icon)
|
||
|
{
|
||
|
GList *icons = NULL;
|
||
|
libr_file *handle;
|
||
|
int ret = false;
|
||
|
|
||
|
if(gtk_ret == NULL)
|
||
|
{
|
||
|
/* Why on earth would you call this without obtaining the handle to the resource? */
|
||
|
return false;
|
||
|
}
|
||
|
if(gtk_builder_new == NULL)
|
||
|
{
|
||
|
/* GtkBuilder was not linked with the application */
|
||
|
return false;
|
||
|
}
|
||
|
/* Obtain the handle to the executable */
|
||
|
if((handle = libr_open(NULL, LIBR_READ)) == NULL)
|
||
|
{
|
||
|
/* "Failed to open this executable (%s) for resources", progname() */
|
||
|
return false;
|
||
|
}
|
||
|
register_internal_handle(handle);
|
||
|
/* Obtain the icons from the ELF binary */
|
||
|
icons = libr_gtk_iconlist(handle);
|
||
|
/* Set the embedded icons as the default icon list (if requested) */
|
||
|
if(icons != NULL && set_default_icon)
|
||
|
gtk_window_set_default_icon_list(icons);
|
||
|
*gtk_ret = libr_gtk_load_internal(handle, BUILDER_SECTION);
|
||
|
if(*gtk_ret == NULL)
|
||
|
goto failed;
|
||
|
if(icons_ret)
|
||
|
*icons_ret = icons;
|
||
|
ret = true;
|
||
|
failed:
|
||
|
if(!ret)
|
||
|
libr_close(handle);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Shared libglade resource loading
|
||
|
*/
|
||
|
GladeXML *libr_glade_load_internal(libr_file *handle, char *resource_name)
|
||
|
{
|
||
|
GladeXML *glade = NULL;
|
||
|
size_t glade_length;
|
||
|
char *glade_data;
|
||
|
|
||
|
/* Obtain the GLADE XML definition */
|
||
|
glade_data = libr_malloc(handle, resource_name, &glade_length);
|
||
|
if(glade_data == NULL)
|
||
|
{
|
||
|
/* Failed to obtain embedded glade file */
|
||
|
goto failed;
|
||
|
}
|
||
|
/* Initialize libglade appropriate for the available version */
|
||
|
glade = libr_new_glade(handle, glade_data, glade_length);
|
||
|
if(glade == NULL)
|
||
|
{
|
||
|
/* Failed to initialize embedded glade file */
|
||
|
goto failed;
|
||
|
}
|
||
|
failed:
|
||
|
free(glade_data);
|
||
|
return glade;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Load the requested libglade resource and any applicable icons
|
||
|
*/
|
||
|
EXPORT_FN int libr_glade_load(GladeHandle **glade_ret, char *resource_name)
|
||
|
{
|
||
|
libr_file *handle;
|
||
|
int ret = false;
|
||
|
|
||
|
if(glade_ret == NULL)
|
||
|
{
|
||
|
/* Why on earth would you call this without obtaining the handle to the resource? */
|
||
|
return false;
|
||
|
}
|
||
|
if(glade_init == NULL)
|
||
|
{
|
||
|
/* libglade was not linked with the application */
|
||
|
return false;
|
||
|
}
|
||
|
/* Obtain the handle to the executable */
|
||
|
if((handle = libr_open(NULL, LIBR_READ)) == NULL)
|
||
|
{
|
||
|
/* "Failed to open this executable (%s) for resources", progname() */
|
||
|
return false;
|
||
|
}
|
||
|
register_internal_handle(handle);
|
||
|
*glade_ret = libr_glade_load_internal(handle, resource_name);
|
||
|
if(*glade_ret == NULL)
|
||
|
goto failed;
|
||
|
ret = true;
|
||
|
failed:
|
||
|
if(!ret)
|
||
|
libr_close(handle);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Automatically load the ".glade" resource and any applicable icons
|
||
|
*/
|
||
|
EXPORT_FN int libr_glade_autoload(GladeHandle **glade_ret, IconList **icons_ret, int set_default_icon)
|
||
|
{
|
||
|
libr_file *handle = NULL;
|
||
|
GList *icons = NULL;
|
||
|
|
||
|
if(glade_ret == NULL)
|
||
|
{
|
||
|
/* Why on earth would you call this without obtaining the handle to the resource? */
|
||
|
return false;
|
||
|
}
|
||
|
if(glade_init == NULL)
|
||
|
{
|
||
|
/* libglade was not linked with the application */
|
||
|
return false;
|
||
|
}
|
||
|
/* Obtain the handle to the executable */
|
||
|
if((handle = libr_open(NULL, LIBR_READ)) == NULL)
|
||
|
{
|
||
|
/* "Failed to open this executable (%s) for resources", progname() */
|
||
|
return false;
|
||
|
}
|
||
|
register_internal_handle(handle);
|
||
|
icons = libr_gtk_iconlist(handle);
|
||
|
/* Set the embedded icons as the default icon list (if requested) */
|
||
|
if(icons != NULL && set_default_icon)
|
||
|
gtk_window_set_default_icon_list(icons);
|
||
|
/* Return the libglade and icon handles for the application */
|
||
|
*glade_ret = libr_glade_load_internal(handle, GLADE_SECTION);
|
||
|
if(icons_ret)
|
||
|
*icons_ret = icons;
|
||
|
return true;
|
||
|
}
|