Ventanas de selección

En numerosas ocasiones nos encontraremos con la necesidad de implementar ventanas de selección comunes a muchas aplicaciones, por ejemplo, ventanas para seleccionar un color, un archivo o un tipo de letra. Construir estas ventanas o cuadros de diálogo cada vez, usando los elementos básicos vistos hasta ahora, puede ser muy costoso y aburrido. Los programadores de GTK+ se dieron cuenta de esto muy pronto y programaron algunos cuadros de diálogo comunes para que todo desarrollador pudiera usarlos, ahorrándose tiempo por una parte y manteniendo una consistencia entre todas las aplicaciones GKT+ por otra. Entre estos cuadros de diálogo se encuentran las típicas funcionalidades de seleccionar un color, un fichero o un tipo de fuente.

GtkFileSelection

La ventana de selección de fichero es quizás el cuadro de diálogo más usado de aquellos que ofrece GTK+ de serie.

Esta ventana de selección tiene el siguiente aspecto:

Figura 13. Ventana de selección de archivo

Ventana de selección de archivo

Consiste en una lista de ficheros, una lista de directorios, un botón con una lista desplegable que nos permite seleccionar un directorio de la ruta actual, y una caja de entrada de datos que muestra el filtro aplicado o el nombre del fichero seleccionado una vez que pinches sobre él. Además, vemos una serie de botones disponibles en la parte superior de la ventana, con los que podremos no sólo seleccionar un archivo, sino también borrarlos y renombrarlos, así como crear nuevos directorios. Cuando creemos una ventana de selección de archivo, podremos también deshabilitar esta fila de botones.

Es posible navegar por los directorios de manera tradicional, pulsando con el ratón sobre los nombres de los mismos o bien usando la lista desplegable con el path actual. Un truco para navegar por el sistema de archivos rápidamente con el teclado, consiste en el uso del tabulador (TAB), que nos permitirá teclear las primeras letras de un nombre de directorio al que queremos entrar, pulsar TAB y la ventana de selección nos colocará directamente en ese directorio, sin tener que teclear el nombre completo. Es lo que se conoce como autocompletamiento - una funcionalidad común en editores como emacs y jed.

Crearemos una ventana de selección de fichero llamando a la función gtk_file_selection_new().

	GtkWidget * gtk_file_selection_new ( const gchar * title );
		

Una vez creada la ventana de selección de fichero, podemos indicar un nombre de fichero a abrir por defecto usando la función gtk_file_selection_set_filename.

	void gtk_file_selection_set_filename ( GtkFileSelection * filesel, 
						const gchar * filename );
		

Cuando el usuario abra una ventana de selección de archivo, podemos hacer que sólo se le muestren determinados tipos de archivo, aplicando un filtro sobre el nombre. Por ejemplo, podemos hacer que sólo muestre los archivos gráficos .png usando la función gtk_file_selection_set_complete() pasándole como segundo parámetro la cadena "*.png".

	void gtk_file_selection_complete ( GtkFileSelection * filesel, 
						const gchar * pattern );
		

Finalmente, para obtener el nombre del fichero que el usuario ha seleccionado, llamaremos a la función gtk_file_selection_get_filename().

	gchar * gtk_file_selection_get_filename ( GtkFileSelection * filesel );
		

Esta función devuelve un puntero a una cadena que contiene el nombre del fichero seleccionado (con el path absoluto).

Veamos un ejemplo completo de creación de un cuadro de diálogo de selección de archivo:

#include <stdlib.h>
#include <gtk/gtk.h>

void OnButton(GtkWidget *pWidget);

int main(int argc,char **argv)
{
    GtkWidget *pWindow;
    GtkWidget *pButton;

    gtk_init(&argc,&argv);

    pWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(pWindow), "GtkFileSelection");
    gtk_window_set_default_size(GTK_WINDOW(pWindow), 320, 200);

    pButton=gtk_button_new_with_mnemonic("_Examinar...");
    gtk_container_add(GTK_CONTAINER(pWindow), pButton);

    g_signal_connect(G_OBJECT(pWindow), "destroy", G_CALLBACK(gtk_main_quit), NULL);
    g_signal_connect(G_OBJECT(pButton), "clicked", G_CALLBACK(OnButton), NULL);

    gtk_widget_show_all(pWindow);
    gtk_main();
    return EXIT_SUCCESS;
}

void OnButton(GtkWidget *pWidget)
{
    GtkWidget *pFileSelection;
    GtkWidget *pDialog;
    const gchar *sPath;

    /* Creación de la ventana de selección */
    pFileSelection = gtk_file_selection_new("Abrir...");
    /* Limitar las acciones a esta ventana */
    gtk_window_set_modal(GTK_WINDOW(pFileSelection), TRUE);

    /* Gestionar ventana */
    switch(gtk_dialog_run(GTK_DIALOG(pFileSelection)))
    {
        case GTK_RESPONSE_OK:
            /* Obtención del path */
            sPath = gtk_file_selection_get_filename(GTK_FILE_SELECTION(pFileSelection));
            pDialog = gtk_message_dialog_new(GTK_WINDOW(pFileSelection),
                GTK_DIALOG_MODAL,
                GTK_MESSAGE_INFO,
                GTK_BUTTONS_OK,
                "Path del fichero :\n%s", sPath);
            gtk_dialog_run(GTK_DIALOG(pDialog));
            gtk_widget_destroy(pDialog);
            break;
        default:
            break;
    }
    gtk_widget_destroy(pFileSelection);
}
      

GtkFileChooser

GtkFileChooser es el nuevo conjunto de APIs (dese GTK+ 2.4) para los widgets y cuadros de diálogo de selección de archivos. Las versiones anteriores de GTK+ usaban GtkFileSelection, que como hemos visto, tenía varias deficiencias y opciones a mejorar.

GtkFileChooser es un interfaz abstracto que puede ser implementado por los widgets que realicen tareas de selección de archivos. Hay dos widgets en GTK+ que implementan este interfaz: GtkFileChooserDialog y GtkFileChooserWidget. La mayoría de las aplicciones necesitan únicamente hacer uso de GtkFileChooserDialog, que es una caja de diálogo que permite al usuario seleccionar un archivo entre todos los existentes para abrirlo, o guardar y nombrar nuevos documentos y archivos. GtkFileChooserWidget es un widget preparado para aplicaciones especiales que requieran empotrar el widget de selección de archivos dentro de una ventana con mayores funcionalidades. Dentro del contexto GTK+, un GtkFileChooserDialog es simplemente una caja GtkDialog con un widget GtkFileChooserWidget dentro de la misma.

Cómo crear un GtkFileChooserDialog

Para crear un GtkFileChooserDialog, simplemente llamaremos a la función gtk_file_chooser_dialog_new(). Esta función es similar a gtk_dialog_new(), tomando como parámetros el titulo del cuadro de diálogo y la ventana padre, así como sus botones. Usa también otro parámetro adicional, que determina si la ventana para la selección de archivos será usada para abrir un fichero existente o para guardar un nuevo archivo.

Véase el siguiente ejemplo, que muestra un uso típico de una ventana de selección de archivos y cómo obtener el nombre de fichero seleccionado:

Figura 14. GtkFileChooser en acción

GtkFileChooser en acción

#include <stdlib.h>
#include <gtk/gtk.h>


void OnButton(GtkWidget *pWidget);

int main(int argc,char **argv)
{
    GtkWidget *pWindow;
    GtkWidget *pButton;

    gtk_init(&argc,&argv);

    pWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(pWindow), "GtkFileChooser");
    gtk_window_set_default_size(GTK_WINDOW(pWindow), 320, 200);

    pButton=gtk_button_new_with_mnemonic("_Examinar...");
    gtk_container_add(GTK_CONTAINER(pWindow), pButton);

    g_signal_connect(G_OBJECT(pWindow), "destroy", G_CALLBACK(gtk_main_quit), NULL);
    g_signal_connect(G_OBJECT(pButton), "clicked", G_CALLBACK(OnButton), NULL);

    gtk_widget_show_all(pWindow);
    gtk_main();
    return EXIT_SUCCESS;
}

void OnButton(GtkWidget *pWidget)
{
    GtkWidget *pFileSelection;
    GtkWidget *pDialog;
    const gchar *sChemin;

    /* Creación de la ventana de selección */
     
    pFileSelection = gtk_file_chooser_dialog_new("Abrir fichero",
                                      NULL,
                                      GTK_FILE_CHOOSER_ACTION_OPEN,
                                      GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                                      GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
                                      NULL);
    /* Limitar las acciones a esta ventana */
    gtk_window_set_modal(GTK_WINDOW(pFileSelection), TRUE);

        if (gtk_dialog_run (GTK_DIALOG (pFileSelection)) == GTK_RESPONSE_ACCEPT)
          {
            char *filename;
        
            filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (pFileSelection));
            g_print ("%s\n", filename);
            g_free (filename);
          }

    gtk_widget_destroy(pFileSelection);
}
      

Modos de selección

GtkFileChooser puede ser usado en dos modos, para seleccionar un fichero único o para seleccionar múltiples ficheros a la vez. Para fijar el comportamiento deseado, usaremos la gtk_file_chooser_set_select_multiple(). En el modo de selección individual, podemos usar la función gtk_file_chooser_get_filename() para obtener el nombre del fichero del sistema de archivos local o gtk_file_chooser_get_uri() para obtener la URI completa del mismo. En el modo de selección múltiple, usaremos gtk_file_chooser_get_filenames() para obtener una lista GSList con los nombres (strings) de los ficheros, o gtk_file_chooser_get_uris() para obtener una lista de las URIs de los mismos.

También es posible configurar GtkFileChooser para seleccionar ficheros o carpetas. Por ejemplo, imaginemos un programa de backup que debe permitir al usuario seleccinar una carpeta de la que hacer una coia de seguridad (incluyendo todos sus subdirectorios). Para determinar si GtkFileChooser será usado para seleccionar ficheros o carpetas, usaremos la función gtk_file_chooser_set_action(). Esta función nos permitirá además configurar si el selector de fichero será usado para seleccionar archivos o carpetas existentes (ej. para implementar "Archivo/Abrir..."), ó para teclear el nombre de un nuevo fichero (ej. para implementar "Archivo/Guardar como...").

Cómo instalar un widget de previsualización

Muchas aplicaciones necesitan tener una facilidad que permita la previsualización de los ficheros a seleccionar dentro de la ventana de selección de archivos. Antes de la versión GTK+ 2.4, el programador debía de acceder directamente a la jerarquía del widget GtkFileSelection para enganchar el código de un widget de previsualización. Con GtkFileChooser, existe un API encargado de facilitar esta acción.

En la siguiente sección de código podemos ver un ejemplo de creación de un widget de previsualización:


{
  GtkImage *preview;

  ...

  preview = gtk_image_new ();

  gtk_file_chooser_set_preview_widget (my_file_chooser, preview);
  g_signal_connect (my_file_chooser, "update-preview",
                    G_CALLBACK (update_preview_cb), preview);
}

static void
update_preview_cb (GtkFileChooser *file_chooser, gpointer data)
{
  GtkWidget *preview;
  char *filename;
  GdkPixbuf *pixbuf;
  gboolean have_preview;

  preview = GTK_WIDGET (data);
  filename = gtk_file_chooser_get_preview_filename (file_chooser);

  pixbuf = gdk_pixbuf_new_from_file_at_size (filename, 128, 128, NULL);
  have_preview = (pixbuf != NULL);
  g_free (filename);

  gtk_image_set_from_pixbuf (GTK_IMAGE (preview), pixbuf);
  if (pixbuf)
    gdk_pixbuf_unref (pixbuf);

  gtk_file_chooser_set_preview_widget_active (file_chooser, have_preview);
}

       

Cómo instalar widgets adicionales

Algunas aplicaciones necesitarán instalar funcionalidades adicionales (widgets extras) en la ventana de selección de archivos. Por ejemplo, una aplicación podría querer ofrecer un botón conmutador para dar la usuario la opción de abrir un fichero como sólo-lectura o lectura-escritura.

Veamos un ejemplo de código que muestra cómo crear widgets extra en la ventana de selección de archivos:


{
  GtkWidget *toggle;

  ...

  toggle = gtk_check_button_new_with_label ("Open file read-only");
  gtk_widget_show (toggle);
  gtk_file_chooser_set_extra_widget (my_file_chooser, toggle);
}

      

Nuevas características

El widget GtkFileChooser incluye nuevas características (respecto a GtkFileSelection) como la siguientes:

  • Posibilidad de seleccionar URIs en vez de simples ficheros locales. Deberás usar una implementación de GtkFileSystem que soporte esto, por ejemplo el backend gnome-vfs.

  • Presentar una lista de accesos directos a carpetas (en función de cada aplicación). Por ejemplo, un programa de diseño gráfico podría querer añadir su propio acceso directo a la carpeta /usr/share/paint_program/Clipart.

  • Definir filtros personalizados de tal forma que no se muestren todos los ficheros de una carpeta. Por ejemplo, podrías querer filtrar los ficheros temporales (de backup) que crean algunas aplicaciones, o mostrar sólo ficheros gráficos.

Para obtener más información sobre cómo usar estas características, podemos consultar la documentación de referencia de GtkFileChooser.

GtkColorSelection

GtkColorSelection es un widget usado para seleccionar un color. Consta de una rueda de color y varias escalas y cajas de entrada de datos para teclear valores que definen un color, como tonalidad, saturación, valor, rojo, verde, azul y opacidad. Este widget podemos encontrarlo en la ventana de selección de color estándar GtkColorSelectionDialog. En las siguientes páginas veremos cómo usar este cuadro de diálogo.

GtkColorSelectionDialog

GtkColorSelectionDialog es el cuadro de diálogo estándar usado en GKT+ para seleccionar un color, al igual que el cuadro de diálogo GtkFileSelection nos ofrecía la posibilidad de seleccionar un fichero.

La función GtkWidget* gtk_color_selection_dialog_new (const gchar *title) toma como parámetro de entrada el texto del título de la ventana que queremos crear y devuelve el propio GtkColorSelectionDialog. La estructura interna de éste widget contiene los siguientes elementos:

  • GtkWidget *colorsel : widget GtkColorSelection que contiene el cuadro de diálogo. Usaremos este widget y su función gtk_color_selection_get_current_color() para acceder al color seleccionado por el usuario. Debemos conectar un manejador para la señal color_changed de este widget, para ser notificados cuando el color cambie.

  • GtkWidget *ok_button : el widget de botón OK contenido en la caja de diálogo. Debemos conectar un manejador al evento clicked.

  • GtkWidget *cancel_button: el widget de botón OK contenido en la caja de diálogo. Debemos conectar un manejador al evento clicked.

  • GtkWidget *help_button: el widget del botón AYUDA contenido en la caja de diálogo. Debemos conectar un manejador al evento clicked.

Por ejemplo, para acceder al botón OK de un cuadro de diálogo GtkColorSelectionDialog lo haríamos así GTK_COLOR_SELECTION_DIALOG (colorseldialog)->ok_button

El widget de selección de color soporta ajustar la opacidad de un color (también conocido como ajuste del canal alpha). Por defecto, esto está deshabilitado. Si llamamos a la función

      void gtk_color_selection_set_has_opacity_control( GtkColorSelection *colorsel,
                                                  gboolean           has_opacity );
      

con has_opacity a TRUE, habilitamos la opción de opacidad. De la misma forma, si has_opacity es FALSE, se deshabilitará.

Es posible establecer el color actual explícitamente mediante la llamada a la función gtk_color_selection_set_current_color() con un puntero a una estructura GdkColor. Se puede establecer la opacidad (canal alpha) mediante gtk_color_selection_set_current_alpha(). El valor de alpha debería de estar entre 0 (totalmente transparente) y 65636 (totalmente opaco). El prototipo de las funciones anteriores es el siguiente:

      void gtk_color_selection_set_current_color( GtkColorSelection *colorsel,
                                            GdkColor          *color );
      

      void gtk_color_selection_set_current_alpha( GtkColorSelection *colorsel,
                                            guint16            alpha );
      

Cuando lo que queremos es obtener el color actual, algo típico cuando recibimos la señal color_changed, usaremos estas funciones:

      void gtk_color_selection_get_current_color( GtkColorSelection *colorsel,
	                                    GdkColor *color );
      
      void gtk_color_selection_get_current_alpha( GtkColorSelection *colorsel,
                                            guint16           *alpha );
      

Lo siguiente es un ejemplo que demuestra el uso del widget ColorSelectionDialog. El programa muestra una ventana que contiene un área de dibujo. Pulsando sobre ella, se abre un cuadro de diálogo de selección de color, donde al seleccionar un color conseguiremos cambiar también el color de fondo del área de dibujo.

Figura 15. Ejemplo de GtkColorSelectionDialog en ejecución

Ejemplo de GtkColorSelectionDialog en ejecución
#include <glib.h>
#include <gdk/gdk.h>
#include <gtk/gtk.h>

GtkWidget *colorseldlg = NULL;
GtkWidget *drawingarea = NULL;
GdkColor color;

/* Color changed handler */

static void color_changed_cb( GtkWidget         *widget,
                              GtkColorSelection *colorsel )
{
  GdkColor ncolor;

  gtk_color_selection_get_current_color (colorsel, &ncolor);
  gtk_widget_modify_bg (drawingarea, GTK_STATE_NORMAL, &ncolor);       
}

/* Drawingarea event handler */

static gboolean area_event( GtkWidget *widget,
                            GdkEvent  *event,
                            gpointer   client_data )
{
  gint handled = FALSE;
  gint response;
  GtkColorSelection *colorsel;

  /* Check if we've received a button pressed event */

  if (event->type == GDK_BUTTON_PRESS)
    {
      handled = TRUE;

       /* Create color selection dialog */
      if (colorseldlg == NULL)
        colorseldlg = gtk_color_selection_dialog_new ("Select background color");

      /* Get the ColorSelection widget */
      colorsel = GTK_COLOR_SELECTION (GTK_COLOR_SELECTION_DIALOG (colorseldlg)->colorsel);

      gtk_color_selection_set_previous_color (colorsel, &color);
      gtk_color_selection_set_current_color (colorsel, &color);
      gtk_color_selection_set_has_palette (colorsel, TRUE);

      /* Connect to the "color_changedsignal, set the client-data
       * to the colorsel widget */
      g_signal_connect (G_OBJECT (colorsel), "color_changed",
                        G_CALLBACK (color_changed_cb), (gpointer) colorsel);

      /* Show the dialog */
      response = gtk_dialog_run (GTK_DIALOG (colorseldlg));

      if (response == GTK_RESPONSE_OK)
        gtk_color_selection_get_current_color (colorsel, &color);
      else 
        gtk_widget_modify_bg (drawingarea, GTK_STATE_NORMAL, &color);

      gtk_widget_hide (colorseldlg);
    }

  return handled;
}

/* Close down and exit handler */

static gboolean destroy_window( GtkWidget *widget,
                                GdkEvent  *event,
                                gpointer   client_data )
{
  gtk_main_quit ();
  return TRUE;
}

/* Main */

gint main( gint   argc,
           gchar *argv[] )
{
  GtkWidget *window;

  /* Initialize the toolkit, remove gtk-related commandline stuff */

  gtk_init (&argc, &argv);

  /* Create toplevel window, set title and policies */

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW (window), "Color selection test");
  gtk_window_set_policy (GTK_WINDOW (window), TRUE, TRUE, TRUE);

  /* Attach to the "deleteand "destroyevents so we can exit */

  g_signal_connect (GTK_OBJECT (window), "delete_event",
                    GTK_SIGNAL_FUNC (destroy_window), (gpointer) window);
  
  /* Create drawingarea, set size and catch button events */

  drawingarea = gtk_drawing_area_new ();

  color.red = 0;
  color.blue = 65535;
  color.green = 0;
  gtk_widget_modify_bg (drawingarea, GTK_STATE_NORMAL, &color);       

  gtk_widget_set_size_request (GTK_WIDGET (drawingarea), 200, 200);

  gtk_widget_set_events (drawingarea, GDK_BUTTON_PRESS_MASK);

  g_signal_connect (GTK_OBJECT (drawingarea), "event", 
                    GTK_SIGNAL_FUNC (area_event), (gpointer) drawingarea);
  
  /* Add drawingarea to window, then show them both */

  gtk_container_add (GTK_CONTAINER (window), drawingarea);

  gtk_widget_show (drawingarea);
  gtk_widget_show (window);
  
  /* Enter the gtk main loop (this never returns) */

  gtk_main ();

  /* Satisfy grumpy compilers */

  return 0;
}
      

GtkFontSelection

GtkFontSelection es un widget para selección de fuentes (tipos de letra). Permite listar todos los tipos disponibles, sus estilos y tamaños, permitiendo al usuario seleccionar una fuente. Usado en conjunto con el widget GtkFontSelectionDialog, que ofrece una ventana ya preparada (cuadro de diálogo) para la selección de fuentes. Para definir qué fuente estará inicialmente seleccionada, usaremo la función gtk_font_selection_set_font_name(). Para obtener la fuente seleccionada, usaremos la función gtk_font_selection_get_font() o gtk_font_selection_get_font_name(). Para cambiar el texto que se muestra en el área de previsualización usaremos la función gtk_font_selection_set_preview_text().

GtkFontSelectionDialog

El widget GtkFontSelectionDialog es el cuadro de diálogo que usaremos normalmente para seleccionar un tipo de fuente.

Para definir desde este cuadro de diálogo qué fuente estará inicialmente seleccionada, usaremos una función con nombre similar a la vista en la sección anterior: gtk_font_selection_dialog_set_font_name(). Para obtener la fuente seleccionada, usaremos la función gtk_font_selection_dialog_get_font() o gtk_font_selection_dialog_get_font_name(). Y finalmente, siguiendo el esquema anterior, para cambiar el texto que se muestra en el área de previsualización usaremos la función gtk_font_selection_dialog_set_preview_text().

La estructura GtkFontSelectionDialog se define de la siguiente forma:

      typedef struct {
      	GtkWidget *ok_button;
	GtkWidget *apply_button;
	GtkWidget *cancel_button;
	} GtkFontSelectionDialog;
      

Donde:

      GtkWidget *ok_button: es el botón OK de la ventana
      
      GtkWidget *apply_button: el botón APLICAR de la ventana.
      Este botón está oculto por defecto, pero es posible mostrarlo/ocultarlo por programación
      
      GtkWidget *cancel_button; 	The Cancel button of the dialog
      

Figura 16. Ejemplo de GtkFontSelectionDialog en ejecución

Ejemplo de GtkFontSelectionDialog en ejecución

En el ejemplo de código siguiente, hemos seleccionado únicamente una sección de código donde se muestra cómo lanzar una ventana de selección de fuente, obtener la fuente que seleccione el usuario y cambiar el tipo de letra que se estaba usando en un área de texto consecuentemente.

static void OkClicked(GtkButton * button, gpointer data)
{
   chosenfont = pango_font_description_from_string( gtk_font_selection_dialog_get_font_name ( GTK_FONT_SELECTION_DIALOG (data)));  
   gtk_widget_destroy (GTK_WIDGET(data));
}

PangoFontDescription * ChooseFont ( gchar * title )
{
  GtkWidget * fontdialog;
  
  fontdialog = gtk_font_selection_dialog_new ( title);

  gtk_signal_connect ( GTK_OBJECT (fontdialog), "destroy",
        GTK_SIGNAL_FUNC(Close), NULL);
        
  gtk_signal_connect (GTK_OBJECT (GTK_FONT_SELECTION_DIALOG (fontdialog ) ->
                        ok_button), "clicked", GTK_SIGNAL_FUNC (OkClicked),
                        fontdialog);

  gtk_signal_connect (GTK_OBJECT (GTK_FONT_SELECTION_DIALOG ( fontdialog) ->
                        cancel_button), "clicked", GTK_SIGNAL_FUNC(CancelClicked),
                        fontdialog);

  chosenfont = NULL;
  gtk_widget_show(fontdialog);
  gtk_window_set_modal(GTK_WINDOW(fontdialog),TRUE);
  gtk_main();
  
  return chosenfont;
 }


void ChangeFont (GtkButton * button, gpointer data)
{
        PangoFontDescription * font;
        
        font = ChooseFont ("Seleccione la fuente a aplicar al texto");
        if (font!=NULL)
           gtk_widget_modify_font(entryarea,font);
        
        pango_font_description_free(font);
}