Toolbar

Extiende el ejemplo anterior agregando una barra de herramientas flotante para cargar o guardar la imagen.

/* vim: set et sw=4 sts=4 :
 *
 * Se compila con:
 * gcc -o toolbar `pkg-config --cflags --libs gtk+-2.0` toolbar.c
 *
 * Copyright (2008) Leandro Lucarella, under BOLA license:
 * http://auriga.wearlab.de/~alb/bola/
 */
 #include <glib.h>
#include <gtk/gtk.h>
 /* forward declarations */
static void destruir(GtkWidget* widget, gpointer data);
static void on_load(GtkWidget* widget, gpointer data);
static void on_save(GtkWidget* widget, gpointer data);
  struct app_t
{
 GtkWidget* ventana;
 GtkWidget* imagen;
 GtkWidget* dialogo_cargar;
 GtkWidget* dialogo_guardar;
 char* filename;
};
 /************************** FUNCIONES DE APP ********************************/
 static void app_init(struct app_t* app)
{
 GtkWidget* toolbar;
 GtkWidget* vbox;
 GtkWidget* handlebox;
  /* por ahora no tenemos un archivo */
 app->filename = NULL;
  /********************************* VENTANA *******************************/
 /* creo ventana principal */
 app->ventana = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  /* conecto la señal "destroy" de la ventana a la callback destruir()
 * esta señal se emite cuando se llama a gtk_widget_destroy() */
 g_signal_connect(G_OBJECT(app->ventana), "destroy", G_CALLBACK(destruir),
 NULL);
  /********************************** VBOX *********************************/
 /* creo un vbox para dividir la ventana en 2 verticalmente, así abajo
 * queda la imagen y arriba la barra de herramientas */
 vbox = gtk_vbox_new(FALSE, 0); /* los hijos no son iguales, 0 espacio) */
  /* agrego vbox a la ventana */
 gtk_container_add(GTK_CONTAINER(app->ventana), vbox);
  /******************************** HANDLEBOX ******************************/
 /* creo un HandleBox para poder desacoplar la barra de herramientas de la
 * ventana */
 handlebox = gtk_handle_box_new();
  /* agregar handlebox a vbox de forma que quede comprimido arriba de todo */
 gtk_box_pack_start(GTK_BOX(vbox), handlebox,
 FALSE, /* expandir? NO */
 FALSE, /* llenar? NO */
 0); /* espaciado */
  /********************************* TOOLBAR *******************************/
 /* crea una barra de herramientas */
 toolbar = gtk_toolbar_new();
  /* agregar botón de Cargar */
 gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
 "Cargar", /* texto para el botón */
 "Carga un mapa", /* texto para el tooltip */
 "Private", /* private tooltip, para usar con GtkTipsQuery */
 gtk_image_new_from_stock("gtk-open", 24), /* icono */
 G_CALLBACK(on_load), /* callback */
 app); /* user data */
  /* agregar botón de Guardar pero del stock */
 gtk_toolbar_insert_stock(GTK_TOOLBAR(toolbar),
 "gtk-save", /* stock id */
 "Guarda un mapa",  /* texto para el tooltip */
 "Private", /* private tooltip, para usar con GtkTipsQuery */
 G_CALLBACK(on_save), /* callback */
 app, /* user data */
 -1); /* posición (-1 es para insertar al final) */
  /* agregar toolbar al handlebox (para que se pueda desacoplar) */
 gtk_container_add(GTK_CONTAINER(handlebox), toolbar);
  /********************************** IMAGEN *******************************/
 /* creo una imagen a partir de un archivo inexistente para que se muestre el
 * ícono de roto */
 app->imagen = gtk_image_new_from_file("/");
  /* agrega la imagen a la ventana (que es un GtkContainer) */
 gtk_container_add(GTK_CONTAINER(vbox), app->imagen);
  /* creo dialogo de cargar */
 app->dialogo_cargar = gtk_file_chooser_dialog_new("Cargar Imagen",
 GTK_WINDOW(app->ventana),
 GTK_FILE_CHOOSER_ACTION_OPEN,
 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
 NULL);
  /* creo dialogo de guardar */
 app->dialogo_guardar = gtk_file_chooser_dialog_new("Guardar Imagen",
 GTK_WINDOW(app->ventana),
 GTK_FILE_CHOOSER_ACTION_SAVE,
 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
 NULL);
  /* pedimos que pregunte por confirmación si el archivo existe */
 gtk_file_chooser_set_do_overwrite_confirmation(
 GTK_FILE_CHOOSER(app->dialogo_guardar),
 TRUE);
}
 static void app_show(struct app_t* app)
{
 /* muestro la ventana y todos sus hijos */
 gtk_widget_show_all(app->ventana);
}
 static void app_destroy(struct app_t* app)
{
 /* liberar el string con el nombre de archivo */
 if (app->filename)
 g_free(app->filename);
  /* liberar dialogos */
 gtk_widget_destroy(app->dialogo_cargar);
 gtk_widget_destroy(app->dialogo_guardar);
}
 static gboolean app_load_image(struct app_t* app)
{
 GtkWidget* dialog = app->dialogo_cargar;
  if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
 {
 /* la primera vez que se llama a load_image() no hay filename, el resto
 * de las veces sí, así que hay que liberar la memoria del filename
 * viejo */
 if (app->filename)
 g_free(app->filename);
 app->filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
 gtk_image_set_from_file(GTK_IMAGE(app->imagen), app->filename);
 gtk_widget_hide(dialog);
 return TRUE;
 }
 gtk_widget_hide(dialog);
 return FALSE;
}
 static void app_save_image(struct app_t* app)
{
 GtkWidget* dialog = app->dialogo_guardar;
  /* ponemos como base, el nombre del archivo abierto */
 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(app->dialogo_guardar),
 app->filename);
  if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
 {
 /* cuando se llama a este evento, siempre había previamente un filename
 * asociado, así que hay que liberarlo incondicionalmente */
 g_free(app->filename);
 app->filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
 gdk_pixbuf_save(gtk_image_get_pixbuf(GTK_IMAGE(app->imagen)),
 app->filename,
 "png", /* formato */
 NULL,   /* error */
 NULL);  /* parámetros opcionales */
 }
  gtk_widget_hide(dialog);
}
 /****************************** CALLBACKS ***********************************/
 /* Función 'callback' para atender la señal "destroy" de la ventana. */
static void destruir(GtkWidget* widget, gpointer data)
{
 g_print ("[recibido el evento destroy]\n");
  /* finaliza el loop de gtk_main() y libera memoria */
 gtk_main_quit();
}
 /* Función callback para cuando se apreta el botón de Cargar */
static void on_load(GtkWidget* widget, gpointer data)
{
 g_print ("[recibido el evento de botón de Cargar de toolbar]\n");
 app_load_image((struct app_t*) data);
}
 /* Función callback para cuando se apreta el botón de Guardar */
static void on_save(GtkWidget* widget, gpointer data)
{
 g_print ("[recibido el evento de botón de Guardar de toolbar]\n");
 app_save_image((struct app_t*) data);
}
  /***************************** PROGRAMA *************************************/
 int main(int argc, char* argv[])
{
 /* GtkWidget almacena cualquier tipo de widget */
 struct app_t app;
  /* inicializamos aplicación */
 gtk_init(&argc, &argv);
  /* inicializamos aplicación */
 app_init(&app);
  /* si apretó cancelar al cargar imagen, salimos */
 if (!app_load_image(&app))
 return 1;
  /* si no, empezamos la aplicación */
 app_show(&app);
  /* comienza el loop de eventos */
 gtk_main();
  /* libera recursos de la aplicación */
 app_destroy(&app);
  return 0;
}