<?xml version="1.0" encoding="utf-8"?>
<page xmlns="http://projectmallard.org/1.0/" xmlns:its="http://www.w3.org/2005/11/its" type="topic" id="image-viewer.js" xml:lang="es">

  <info>
  <title type="text">Visor de imágenes (JavaScript)</title>
    <link type="guide" xref="js#examples"/>

    <desc>Algo más que una sencilla aplicación «Hola mundo»; escriba un visor de imágenes en GTK+. Incluye una introducción al lenguaje JavaScript.</desc>

    <revision pkgversion="0.1" version="0.1" date="2011-03-19" status="review"/>
    <credit type="author">
      <name>Jonh Wendell</name>
      <email its:translate="no">jwendell@gnome.org</email>
    </credit>
    <credit type="author">
      <name>Johannes Schmid</name>
      <email its:translate="no">jhs@gnome.org</email>
    </credit>
    <credit type="editor">
      <name>Marta Maria Casetti</name>
      <email its:translate="no">mmcasettii@gmail.com</email>
      <years>2013</years>
    </credit>
  
    <mal:credit xmlns:mal="http://projectmallard.org/1.0/" type="translator copyright">
      <mal:name>Daniel Mustieles</mal:name>
      <mal:email>daniel.mustieles@gmail.com</mal:email>
      <mal:years>2011 - 2020</mal:years>
    </mal:credit>
  
    <mal:credit xmlns:mal="http://projectmallard.org/1.0/" type="translator copyright">
      <mal:name>Nicolás Satragno</mal:name>
      <mal:email>nsatragno@gmail.com</mal:email>
      <mal:years>2012 - 2013</mal:years>
    </mal:credit>
  
    <mal:credit xmlns:mal="http://projectmallard.org/1.0/" type="translator copyright">
      <mal:name>Jorge González</mal:name>
      <mal:email>jorgegonz@svn.gnome.org</mal:email>
      <mal:years>2011</mal:years>
    </mal:credit>
  </info>

<title>Visor de imágenes</title>

<synopsis>
  <p>En este tutorial se va a escribir una aplicación GTK+ muy sencilla que carga un archivo e imagen y lo muestra. Aprenderá a:</p>
  <list>
    <item><p>Escribir una interfaz GTK+ de usuario básica usando JavaScript</p></item>
    <item><p>Trabajar con eventos conectando señales a gestores de señales</p></item>
    <item><p>La disposición de las interfaces de usuario usando contenedores</p></item>
    <item><p>Cargar y mostrar archivos de imagen</p></item>
  </list>
  <p>Necesitará lo siguiente para poder seguir este tutorial:</p>
  <list>
    <item><p>Una copia instalada del <link xref="getting-ready">EID Anjuta</link></p></item>
    <item><p>Una copia instalada del intérprete <em>gjs</em></p></item>
    <item><p>Conocimiento básico de cualquier lenguaje de programación orientado a objetos</p></item>
  </list>
</synopsis>

<media type="image" mime="image/png" src="media/image-viewer.png"/>

<section id="anjuta">
  <title>Crear un proyecto en Anjuta</title>
  <p>Antes de empezar a programar, deberá configurar un proyecto nuevo en Anjuta. Esto creará todos los archivos que necesite para construir y ejecutar el código más adelante. También es útil para mantener todo ordenado.</p>
  <steps>
    <item>
    <p>Inicie Anjuta y pulse <guiseq><gui>Archivo</gui><gui>Nuevo</gui><gui>Proyecto</gui></guiseq> para abrir el asistente de proyectos.</p>
    </item>
    <item>
    <p>Elija <gui>Javascript genérico</gui> de la pestaña <gui>JS</gui>, pulse <gui>Adelante</gui> y rellene los detalles en las siguientes páginas. Use <file>visor-imágenes</file> como nombre de proyecto y de carpeta.</p>
   	</item>
    <item>
    <p>Pulse <gui>Aplicar</gui> y se creará el proyecto. Abra <file>src/main.js</file> desde las pestañas <gui>Proyecto</gui> o <gui>Archivo</gui>. Contiene un ejemplo de código muy básico:</p>
    </item>
  </steps>
</section>


<section id="js">
  <title>JavaScript básico: hola mundo</title>
  <p>Antes de empezar a escribir el visor de imágenes, averigüe algo más sobre cómo se usa JavaScript en GNOME. Por supuesto que su primer contacto con un lenguaje de programación debe ser el programa «Hola mundo», que se puede encontrar en el archivo <file>main.js</file>:</p>
  <code mime="application/javascript">print ("¡Hola mundo!");</code>
  <p>Esto debería resultarle natural si está familiarizado con la mayoría del resto de lenguajes de programación. La función <code>print</code> se llama con el argumento <code>"¡Hola mundo!"</code>, que se mostrará en la pantalla. Tenga en cuenta que cada línea de código termina en «;».</p>
</section>

<section id="classes">
  <title>Clases en JavaScript</title>
  <p>Esta es la manera estándar de definir una clase en JavaScript:</p>
  <code mime="application/javascript" style="numbered">
function MyClass () {
  this._init ();
}

MyClass.prototype = {

  _init: function () {
    this.propertyA = "This is an object's field";
    this.propertyB = 10;
  },

  aMethod: function (arg1, arg2) {
    print ("inside aMethod: " + arg1 + " " + arg2);
  },

  dumpProperties: function () {
    print (this.propertyA);
    print (this.propertyB);
  }

}</code>
  <p>Esto define una clase llamada <code>MyClass</code>. Vea cada una de las partes de la definición de la clase:</p>
  <steps>
    <item>
    <p><code>function MyClass</code> es el constructor de la clase; su nombre debe coincidir con el de la clase. Puede acceder a cualquier miembro de la clase usando el objeto <code>this</code>; aquí, el constructor llama al método <code>_init</code> de la clase.</p>
    </item>
    <item>
    <p>El bloque <code>MyClass.prototype</code> es donde define la <em>estructura</em> de la clase. Cada clase se compone de métodos (funciones) y campos (variables); en este ejemplo, hay tres métodos y dos campos.</p>
    </item>
    <item>
    <p>El primer método definido se llama <code>_init</code>, y se especifica que es una función sin argumentos:</p>
    <code>_init: function ()</code>
    <p>Se escribe la función entre corchetes. Aquí se definen dos campos, <code>propertyA</code> y <code>propertyB</code>. La primera se establece como una cadena, y la segunda se establece como un entero (10). La función no retorna ningún valor.</p>
    </item>
    <item>
    <p>El siguiente método se llama <code>aMethod</code> y tiene dos argumentos que imprime cuando se invoca. El último método es <code>dumpProperties</code>, que imprime los campos <code>propertyA</code> y <code>propertyB</code>.</p>
    </item>
    <item>
    <p>Tenga en cuenta cómo se ordena la definición de la clase (prototipo); cada definición de función se separa con una coma.</p>
    </item>
  </steps>

  <p>Ahora que «MyClass» se ha definido, se puede jugar con ella:</p>
  <code mime="application/javascript" style="numbered">
var o = new MyClass ();
o.aMethod ("Hello", "world");
o.propertyA = "Just changed its value!";
o.dumpProperties ();</code>
  <p>Este código crear una instancia nueva de la clase llamada <code>o</code>, ejecuta <code>aMethod</code>, cambia <code>propertyA</code> a una cadena diferente y entonces llama a <code>dumpProperties</code> (que muestra los campos).</p>
  <p>Guarde el código en el archivo <file>main.js</file> y ejecútelo usando <guiseq><gui>Ejecutar</gui><gui>Ejecutar</gui></guiseq> desde el menú o usando la barra de herramientas.</p>
</section>

<section id="gtk">
  <title>Una primera aplicación en GTK</title>
  <p>Vea como queda una aplicación GTK+ muy básica en JavaScript:</p>
  <code mime="application/javascript" style="numbered">
const Gtk = imports.gi.Gtk;

Gtk.init (null, null);

var w = new Gtk.Window ({title: "Image Viewer Demo"});
w.show ();

Gtk.main ();</code>
  <p>Eche un vistazo a lo que está pasando:</p>
  <list>
    <item>
    <p>La primera línea importa el espacio de nombres de GTK+ (es decir, incluye la biblioteca GTK+). Las bibliotecas las proporciona «GObject Introspection (gi)», que implementa asociaciones de lenguajes para la mayoría de las bilbiotecas de GNOME.</p>
    </item>
    <item>
    <p><code>Gtk.init</code> inicializa la biblioteca GTK; esta sentencia es obligatoria para todos los programas GTK.</p>
    </item>
    <item>
    <p>La siguiente línea crea la ventana principal creando un objeto <code>Gtk.Window</code>. Puede pasar varias propiedades al constructor de la ventana usando la sintaxis <code>{property: value, property: value, ...}</code>. En este caso, se está estableciendo el título de la ventana.</p></item>
    <item><p>La siguiente línea muestra explícitamente la ventana. En GTK+, cada widget está oculto de manera predeterminada.</p></item>
    <item><p>Finalmente, <code>Gtk.main</code> ejecuta el bucle principal; en otras palabras, ejecuta el programa. El bucle principal escucha los eventos (señales) de la interfaz del usuario y llama a un gestor de la señal que hará algo útil. En breve aprenderá más cosas sobre las señales.</p></item>
  </list>

  <p>Guarde el código en <file>main.js</file> y ejecútelo. Notará que la aplicación no termina cuando cierra la ventana. Esto es porque todavía no se ha configurado un gestor de señal para gestionar la señal <code>destroy</code> (cerrar) de la ventana. Esto se hará en breve, pero por el momento puede pulsar <keyseq><key>Ctrl</key><key>C</key></keyseq> en la ventana de la terminal para salir del programa.</p>

</section>

<section id="classes2">
  <title>Añadir clases</title>
  <p>La manera correcta de programar en GTK+ es usando clases. Reescriba el código que acaba de escribir usando clases:</p>
  <code mime="application/javascript" style="numbered">
const Gtk = imports.gi.Gtk;

function ImageViewer () {
  this._init ();
}

ImageViewer.prototype = {
  _init: function () {
    this.window = new Gtk.Window ({title: "Image Viewer Demo"});
    this.window.show ();
  }
}

Gtk.init (null, null);
var iv = new ImageViewer ();
Gtk.main ();</code>
  <!-- FIXME: Throws an error, "JS ERROR: !!!   Unhandled type int32 releasing GArgument" on Ubuntu 10.10 -->
  <p>Tenga en cuenta que el programa es el mismo; simplemente se ha movido el código de creación de la ventana a la clase <code>ImageViewer</code>. El constructor de la clase llama al método <code>_init</code>, que crea y muestra la ventana. Se crea entonces una instancia de la clase antes de ejecutar el bucle principal (<code>Gtk.main</code>).</p>
  <p>El código es modular y se puede dividir el varios archivos fácilmente. Esto hace que sea más limpio y fácil de leer.</p>
</section>

<section id="signals">
  <title>Señales</title>
  <p>Las señales son un concepto clave en la programación en GTK. Cuando pasa algo en un objeto, emite una señal; por ejemplo, cuando se pulsa un botón, emite la señal <code>clicked</code>. Si quiere que su programa haga algo cuando ocurre ese evento, debe conectar una función (un «gestor de la señal») a esa señal. Aquí hay un ejemplo:</p>
  <code mime="application/javascript" style="numbered">
function button_clicked () {
  print ("you clicked me!");
}
var b = new Gtk.Button ({label:"Click me"});
b.connect ("clicked", button_clicked);</code>
  <p>Las dos últimas líneas crean un <code>Gtk.Button</code> llamado <code>b</code> y conectan su señal <code>clicked</code> con la función <code>button_clicked</code> que se ha definido anteriormente. Cada vez que se pulsa un botón, se ejecuta el código de la función <code>button_clicked</code>. Esto sólo imprime un mensaje aquí.</p>
  <p>La sintaxis para conectar cualquier señal a una función es:</p>
  <code mime="application/javascript">
object.connect (&lt;signal_name&gt;, &lt;function_to_be_called&gt;);</code>
  <p>Puede encontrar definiciones de señales para cualquier objeto en la <link href="https://developer.gnome.org/gtk3/stable/gtkobjects.html">referencia de clases de GTK+</link>.</p>

  <note>
    <p>Puede simplificar el código usando una definición de función en línea:</p>
    <code mime="application/javascript">
b.connect ("clicked", function () { print ("you clicked me!"); });</code>
  </note>

</section>

<section id="close">
  <title>Cerrar la ventana</title>
  <p>Cuando cierra una ventana de GTK, realmente no se cierra, se oculta. Esto le permite mantener la ventana (lo que es útil si quiere preguntar al usuario si realmente quiere cerrar la ventana, por ejemplo).</p>
  <p>En este caso, simplemente se quiere cerrar la ventana. La manera más sencilla de hacerlo es conectar la señal <code>hide</code> del objeto GtkWindow a una función que cierra la aplicación. Vuelva al archivo <file>image-viewer.js</file> y añada el siguiente código al método <code>_init</code>, en la línea anterior a <code>this.window.show</code>:</p>
  <code mime="application/javascript" style="numbered">this.window.connect ("hide", Gtk.main_quit);</code>
  <p>Esto conecta la señal <code>hide</code> de la ventana con la función <code>main_quit</code> de GTK, que termina la ejecución del bucle principal de GTK. Una vez que el bucle principal termina, la función <code>Gtk.main</code> retorna. El programa puede continuar ejecutando cualquier código que haya después de la línea <code>Gtk.main ();</code>, pero como no hay ningún código después de ese punto, el programa simplemente termina.</p>
</section>

<section id="containers2">
  <title>Contenedores: diseñar la interfaz de usuario</title>
  <p>Los widgets (controles, como botones y etiquetas) se pueden organizar en la ventana usando <em>contenedores</em>. Puede organizar el diseño mezclando diferentes tipos de contenedores, como cajas y rejillas.</p>
  <p>Una <code>Gtk.Window</code> es en sí misma un tipo de contenedor, pero sólo puede poner un widget directamente en ella. Se quieren poner dos widgets, una imagen y un botón, por lo que se necesita poner un contenedor «de mayor capacidad» dentro de la ventana para que contenga otros widgets. Hay varios <link href="http://library.gnome.org/devel/gtk/stable/GtkContainer.html">tipos de contenedores</link> disponibles, pero aquí se usará una <code>Gtk.Box</code>. Una <code>Gtk.Box</code> puede contener varios widgets, organizados horizontal o verticalmente. Se pueden hacer diseños más complejos poniendo varias cajas dentro de otras, y así sucesivamente.</p>
  <note>
  <p>Hay un diseñador de interfaces gráficas llamado <app>Glade</app> integrado en <app>Anjuta</app> que hace que el diseño de IU sea realmente fácil. Sin embargo, para este ejemplo, se va a codificar todo manualmente.</p>
  </note>
  <p>Añada la caja y los widgets a la ventana. Inserte el siguiente código en el método <code>_init</code>, justo debajo de la línea <code>this.window.show</code>:</p>
  <code mime="application/javascript" style="numbered">
var main_box = new Gtk.Box ({orientation: Gtk.Orientation.VERTICAL, spacing: 0});
this.window.add (main_box);</code>
  <p>La primera línea crea una <code>Gtk.Box</code> llamada <code>main_box</code> y establece dos de sus propiedades: la <code>orientation</code> se establece a «vertical» (por lo que los widgets se ordenan en columna), y el <code>spacing</code> entre los widgets, que se establece a 0 píxeles. La siguiente línea añade la <code>Gtk.Box</code> recién creada a la ventana.</p>
  <p>De momento, la ventana sólo contiene una <code>Gtk.Box</code> vacía, y si ejecuta el programa ahora no verá ningún cambio (la <code>Gtk.Box</code> es un contenedor transparente, por lo que no puede que está ahí).</p>
</section>

<section id="packing2">
  <title>Empaquetado: añadir widgets al contenedor</title>
  <p>Para añadir algunos widgets a la <code>Gtk.Box</code>, inserte el siguiente código justo debajo de la línea <code>this.window.add (main_box)</code>:</p>
  <code mime="application/javascript" style="numbered">
this.image = new Gtk.Image ();
main_box.pack_start (this.image, true, true, 0);</code>
  <p>La primera línea crea una <code>Gtk.Image</code> nueva llamada <code>image</code>, que se usará para mostrar un archivo de imagen. Entonces, se añade el widget de imagen (<em>packed</em>) al contenedor <code>main_box</code> usando el método <link href="http://library.gnome.org/devel/gtk/stable/GtkBox.html#gtk-box-pack-start"><code>pack_start</code></link> de <code>Gtk.Box</code>.</p>
  <p><code>pack_start</code> toma 4 argumentos: el widget que añadir a la <code>Gtk.Box</code> (<code>child</code>); indica si la <code>Gtk.Box</code> debe crecer a lo largo cuando se añade el widget nuevo (<code>expand</code>); indica si el widget nuevo debe tomar todo el espacio adicional creado si la <code>Gtk.Box</code> se hace más grande (<code>fill</code>); y cuánto espacio debe haber, en píxeles, entre el widget y los demás widgets dentro de la <code>Gtk.Box</code> (<code>padding</code>).</p>
  <p>Los contenedores (y los widgets) de GTK+ se expanden dinámicamente, si les deja, para rellenar el espacio disponible. No posicione widgets indicando unas coordenadas x-y precisas en la ventana; en lugar de eso, se posicionan relativos a otro. Esto hace que el manejo de la redimensión de las ventanas sea más fácil, y que los widgets tengan un tamaño sensible automática en la mayoría de las situaciones.</p>
  <p>También tenga en cuenta cómo se organizan los widgets de manera jerárquica. Una vez empaquetados en la <code>Gtk.Box</code>, la <code>Gtk.Image</code> se considera un <em>hijo</em> de la <code>Gtk.Box</code>. Esto le permite tratar a todos los hijos de un widget como un grupo; por ejemplo, puede ocultar la <code>Gtk.Box</code>, lo que haría que también se ocultaran todos sus hijos a la vez.</p>
  <p>Ahora inserte estas dos líneas, justo debajo de las dos que acaba de añadir.</p>
  <code mime="application/javascript" style="numbered">
var open_button = new Gtk.Button ({label: "Open a picture..."});
main_box.pack_start (open_button, false, false, 0);</code>
  <p>Estas líneas son similares a las dos primeras, pero esta vez crean un <code>Gtk.Button</code> y lo añaden a la <code>main_box</code>. Tenga en cuenta que aquí se está estableciendo el argumento <code>expand</code> (el segundo) a <code>false</code>, mientras que para la <code>Gtk.Image</code> se estableció a <code>true</code>. Esto hará que la imagen tome todo el espacio disponible y que el botón tome sólo el espacio que necesite. Cuando se maximiza la ventana, el tamaño del botón será el mismo, pero el tamaño de la imagen aumentará junto con el resto de la ventana.</p>
  <p>Finalmente, se debe cambiar la línea <code>this.window.show ();</code> para leer:</p>
  <code>this.window.show_all ();</code>
  <p>Esto mostrará el hijo de la ventana de GTK, y todos sus hijos, y los hijos de sus hijos, etcétera. (Recuerde que los widgets de GTK están ocultos de manera predeterminada.)</p>
</section>

<section id="loading2">
  <title>Cargar la imagen: conectar con la señal <code>clicked</code> del botón</title>
  <p>Cuando el usuario pulse en el botón <gui>Abrir</gui>, debe aparecer un diálogo en el que el usuario pueda elegir una imagen. una vez elegida, la imagen se debe cargar y mostrar en el widget de imagen.</p>
  <p>El primer paso es conectar la señal <code>clicked</code> del botón a una función gestora de la señal, llamada <code>_openClicked</code>. Ponga este código inmediatamente después de la línea <code>var open_button = new Gtk.Button</code> en la que se creó el botón:</p>
  <code mime="application/javascript">
open_button.connect ("clicked", Lang.bind (this, this._openClicked));</code>
  <p>Aquí se está usando el JavaScript auxiliar <em>Lang</em>. Permite conectar un <em>método de clase</em> a la señal, en vez de una función normal (sin clase) que se haya usado anteriormente para señal <code>hide</code> de la ventana. No se preocupe por esto ahora, es sólo un detalle técnico. Para que funcione, debe poner también la siguiente línea en la parte superior del archivo:</p>
  <code mime="application/javascript">const Lang = imports.lang;</code>
</section>

<section id="loading3">
  <title>Cargar la imagen: escribir la llamada de retorno de la señal</title>
  <p>Ahora se puede crear el método <code>_openClicked()</code>. Inserte el siguiente código en el bloque de código de <code>ImageViewer.prototype</code>, después del método <code>_init</code> (y no olvide la coma):</p>
    <code mime="application/javascript" style="numbered">
  _openClicked: function () {
    var chooser = new Gtk.FileChooserDialog ({title: "Select an image",
                                              action: Gtk.FileChooserAction.OPEN,
                                              transient_for: this.window,
                                              modal: true});
    chooser.add_button (Gtk.STOCK_CANCEL, 0);
    chooser.add_button (Gtk.STOCK_OPEN, 1);
    chooser.set_default_response (1);

    var filter = new Gtk.FileFilter ();
    filter.add_pixbuf_formats ();
    chooser.filter = filter;

    if (chooser.run () == 1)
      this.image.file = chooser.get_filename ();

    chooser.destroy ();
  }</code>
  <p>Esto es un poco más complicado que todo lo que se ha intentado hasta ahora, así que se puede desglosar:</p>
  <list>
    <item>
      <p>La línea que comienza por <code>var chooser</code> crear un diálogo <gui>Abrir</gui>, que el usuario puede utilizar para elegir archivos. Se establecen cuatro propiedades: el título del diálogo, la acción (tipo) del diálogo (es un diálogo «open», pero se podría haber usado <code>SAVE</code> si la intención hubiese sido guardar un archivo; <code>transient_for</code>, que establece la ventana padre del diálogo; y <code>modal</code>, que si se establecer a <code>true</code> evita que el usuario pueda pulsar en otra área de la aplicación hasta que no se cierre el diálogo.</p>
    </item>
    <item>
    <p>Las dos siguientes líneas añaden los botones <gui>Cancelar</gui> y <gui>Abrir</gui> al diálogo. el segundo argumento del método <code>add_button</code> es el valor (entero) que se devuelve cuando se pulsa el botón: 0 para <gui>Cancelar</gui> y 1 para <gui>Abrir</gui>.</p>
    <p>Note que se está usando nombres de botones del <em>almacén</em> de GTK, en lugar de escribir manualmente «Cancelar» o «Abrir». La ventaja de usar nombres del almacén es que las etiquetas de los botones ya estarán traducidas en el idioma del usuario.</p>
    </item>
    <item>
    <p><code>set_default_response</code> determina qué botón se activará si el usuario hace una doble pulsación o presiona <key>Intro</key>. En este caso, se usa el botón <gui>Abrir</gui> como predeterminado (que tiene el valor «1»).</p>
    </item>
    <item>
    <p>Las tres líneas siguientes restringen el diálogo <gui>Abrir</gui> para que sólo muestre archivos que se puedan abrir con <code>Gtk.Image</code>. Primero se crea un objeto de filtro; luego se añaden los tipos de archivos soportados por el <code>Gdk.Pixbuf</code> (que incluye la mayoría de los formatos de imagen como PNG y JPEG) al filtro. Por último, se establece que este filtro sea el filtro del diálogo <gui>Abrir</gui>.</p>
    </item>
    <item>
    <p><code>chooser.run</code> muestra el diálogo <gui>Abrir</gui>. El diálogo esperará a que el usuario elija una imagen; cuando lo haga, <code>chooser.run</code> devolverá el valor <output>1</output> (devolvería <output>0</output> si el usuario pulsara <gui>Cancelar</gui>). La sentencia <code>if</code> comprueba esto.</p>
    </item>
    <item><p>Asumiendo que el usuario pulsó <gui>Abrir</gui>, la siguiente línea establece la propiedad <code>file</code> de la <code>Gtk.Image</code> al nombre del archivo de imagen seleccionada por el usuario. La <code>Gtk.Image</code> cargará y mostrará la imagen elegida.</p>
    </item>
    <item>
    <p>En la última línea de este método se destruye el diálogo <gui>Abrir</gui> porque ya no se necesita.</p>
    </item>
  </list>

  </section>

<section id="run">
  <title>Ejecutar la aplicación</title>
  <p>Todo el código que necesita debe estar en su lugar, así que trate de ejecutar el código. Esto debería ser todo; un visor de imágenes completamente funcional (y un completo tour sobre JavaScript y GTK+) en poco tiempo.</p>
</section>

<section id="impl">
 <title>Implementación de referencia</title>
 <p>Si tiene problemas con este tutorial, compare su código con este <link href="image-viewer/image-viewer.js">código de referencia</link>.</p>
</section>

<section id="next">
  <title>Siguientes pasos</title>
  <p>Aquí hay algunas ideas sobre cómo puede extender esta sencilla demostración:</p>
  <list>
   <item>
   <p>Haga que el usuario selecciona una carpeta en vez de un archivo, y proporcione controles para moverse por todas las imágenes de una carpeta.</p>
   </item>
   <item>
   <p>Aplicar filtros aleatorios y efectos a la imagen cuando se carga y permitir al usuario guardar la imagen modificada.</p>
   <p><link href="http://www.gegl.org/api.html">GEGL</link> proporciona la capacidad de manipular imágenes de manera potente.</p>
   </item>
   <item>
   <p>Permitir al usuario cargar imágenes desde recursos de red compartidos, escáneres y otras fuentes más complicadas.</p>
   <p>Puede usar <link href="http://library.gnome.org/devel/gio/unstable/">GIO</link> para gestionar transferencias de archivos de red y similares, y <link href="http://library.gnome.org/devel/gnome-scan/unstable/">GNOME Scan</link> para gestionar el escaneado.</p>
   </item>
  </list>
</section>

</page>
