Le selezioni sono un tipo di comunicazione tra processi
supportato da GTK. Una selezione identifica un frammento di dati; per
esempio, una porzione di testo selezionata dall'utente in qualche modo,
magari con il mouse. Su un display solo un'applicazione alla volta
(il proprietario) puó essere proprietaria di una
particolare selezione, sicché quando un'applicazione richiede
una selezione il precedente proprietario deve comunicare all'utente che
la selezione è stata ceduta. Altre applicazioni possono richiedere
il contenuto di una selezione in diverse forme, chiamate obiettivi.
Ci può essere un numero qualsiasi di selezioni, ma la maggior parte
delle applicazioni X può gestirne solo una, la selezione
primaria.
Nella maggior parte dei casi per una applicazione GTK non è
necessario gestire esplicitamente le selezioni. I widget standard,
come quello di Ingresso, hanno già la capacità di
chiedere la selezione se necessario (p. e., quando l'utente
seleziona sul testo), e di recuperare il contenuto di una selezione
di un altro widget o di un'altra applicazione (p. e., quando l'utente
clicca il tasto centrale del mouse). Ci possono comunque essere dei
casi nei quali si vuole dare ad altri widget la capacità di
fornire la selezione, o si vogliono recuperare degli obiettivi non
supportati direttamente.
Un concetto fondamentale necessario per comprendere la gestione delle
selezioni è quello di atomo. Un atomo è un intero
che identifica univocamente una stringa (su un certo display).
Certi atomi sono predefiniti dal server X, e in alcuni casi in gtk.h
ci sono costanti corrispondenti a questi atomi. Per esempio, la costante
GDK_PRIMARY_SELECTION corrisponde alla stringa ``PRIMARY''.
Negli altri casi bisogna usare le funzioni gdk_atom_intern()
per ottenere l'atomo corrispondente ad una stringa, e gdk_atom_name()
per ottenere il nome di un atomo. Sia le selezioni sia gli obiettivi sono
identificati da atomi.
Questo converte la selezione nella forma specificata
dall'obiettivo target. Se possibile, il campo time
dovrebbe essere il tempo dell'evento che ha attivato la selezione.
Questo aiuta a far si che gli eventi avvengano nell'ordine in cui
l'utente li ha richiesti. Se comunque non fosse disponibile (per
esempio, se la conversione è stata attivata da un segnale di
``cliccato''), allora si può usare la costante
GDK_CURRENT_TIME.
Quando il proprietario di una selezione risponde ad una richiesta,
un segnale ``selection_received'' (selezione ricevuta) viene inviato
alla vostra applicazione. Il gestore di questo segnale riceve un
puntatore ad una struttura GtkSelectionData, che è
definita nel modo seguente:
selection e target sono i valori da voi specificati
nella chiamata gtk_selection_convert(). type è
un atomo che identifica il tipo di dati restituiti dal proprietario della
selezione. Alcuni valori possibili sono ``STRING'', una stringa di
caratteri latin-1, ``ATOM'', una serie di atomi, ``INTEGER'', un intero, ecc.
La maggior parte degli obiettivi può restituire solo un tipo.
format ci dà la lunghezza delle unità (per esempio caratteri)
in bit. Di solito, quando si ricevono i dati non ci si cura di questo.
data è un puntatore ai dati restituiti, e length
è la lunghezza dei dati restituiti, in byte. Se length
è negativo allora si è verificato un errore e non è
stato possibile recuperare la selezione. Questo può avvenire se
nessuna applicazione era proprietaria della selezione, o se si è
richiesto un obiettivo non supportato dall'applicazione. Viene garantito
che il buffer sia un byte più lungo di length; il byte
in più sarà sempre zero, in modo che non sia necessario
ricopiare le stringhe solo per farle terminare con zero.
Nell'esempio che segue viene recuperato l'obiettivo speciale ``TARGETS'',
che è una lista di tutti gli obiettivi in cui può essere
convertita la selezione.
/* gettargets.c */
#include <gtk/gtk.h>
void selection_received (GtkWidget *widget,
GtkSelectionData *selection_data,
gpointer data);
/* Gestore di segnale chiamato quando l'utente clicca nel bottone */
/* "Get Targets" */
void
get_targets (GtkWidget *widget, gpointer data)
{
static GdkAtom targets_atom = GDK_NONE;
/* Prende l'atomo corrispondente alla stringa "TARGETS" */
if (targets_atom == GDK_NONE)
targets_atom = gdk_atom_intern ("TARGETS", FALSE);
/* E richiede l'obiettivo "TARGETS" per la selezione primaria */
gtk_selection_convert (widget, GDK_SELECTION_PRIMARY, targets_atom,
GDK_CURRENT_TIME);
}
/* Gestore di segnale chiamato quando il proprietario della selezione */
/* restituisce i dati */
void
selection_received (GtkWidget *widget, GtkSelectionData *selection_data,
gpointer data)
{
GdkAtom *atoms;
GList *item_list;
int i;
/* **** IMPORTANTE **** Controlla che il recupero sia riuscito */
if (selection_data->length < 0)
{
g_print ("Selection retrieval failed\n");
return;
}
/* Make sure we got the data in the expected form */
if (selection_data->type != GDK_SELECTION_TYPE_ATOM)
{
g_print ("Selection \"TARGETS\" was not returned as atoms!\n");
return;
}
/* Stampa gli atomi ricevuti */
atoms = (GdkAtom *)selection_data->data;
item_list = NULL;
for (i=0; i<selection_data->length/sizeof(GdkAtom); i++)
{
char *name;
name = gdk_atom_name (atoms[i]);
if (name != NULL)
g_print ("%s\n",name);
else
g_print ("(bad atom)\n");
}
return;
}
int
main (int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *button;
gtk_init (&argc, &argv);
/* Create the toplevel window */
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), "Event Box");
gtk_container_border_width (GTK_CONTAINER (window), 10);
gtk_signal_connect (GTK_OBJECT (window), "destroy",
GTK_SIGNAL_FUNC (gtk_exit), NULL);
/* Crea un bottone che l'utente può cliccare per ottenere gli obiettivi */
button = gtk_button_new_with_label ("Get Targets");
gtk_container_add (GTK_CONTAINER (window), button);
gtk_signal_connect (GTK_OBJECT(button), "clicked",
GTK_SIGNAL_FUNC (get_targets), NULL);
gtk_signal_connect (GTK_OBJECT(button), "selection_received",
GTK_SIGNAL_FUNC (selection_received), NULL);
gtk_widget_show (button);
gtk_widget_show (window);
gtk_main ();
return 0;
}
Fornire la selezione è un po' più complicato. Bisogna
registrare i gestori che verranno chiamati quando viene richiesta la
propria selezione. Per ogni coppia selezione/obiettivo che si gestirà
occorre una chiamata a:
widget, selection, e target identificano le richieste
che questo gestore soddisferà. remove_func, se non è
NULL, verrà chiamato quando il gestore di segnale viene rimosso.
Questo è utile, per esempio, per linguaggi interpretati ai quali
serve di tener traccia di un conteggio di riferimento per data.
La GtkSelectionData è la stessa di prima, ma stavolta siamo
responsabili di riempire i campi type, format, data,
e length. (Il campo format qui è effettivamente
importante - il server X lo usa per capire se occorre che i byte
dei dati vengano scambiati o no. Di solito sarà 8 - cioè
un carattere - o 32 - cioè un intero.) Questo viene fatto
chiamando la funzione:
Questa funzione si prende cura di fare propriamente una copia dei dati
in modo che non ci si debba preoccupare di conservarli (è opportuno
evitare di riempire a mano i campi della struttura GtkSelectionData).
Quando richiesto dall'utente, richiederete la proprietà della selezione
chiamando:
Se un'altra applicazione richiede la proprietà della selezione,
riceverete un evento di azzeramento della selezione (``selection_clear_event'').
Come esempio di fornitura della selezione, il programma seguente aggiunge
la funzionalità di selezione a un bottone di attivazione. Quando il
bottone viene premuto, il programma richiede la selezione primaria.
L'unico obiettivo supportato (oltre a certi obiettivi come ``TARGETS''
fornito dalla stessa GTK) è l'obiettivo ``STRING''. Quando viene
richiesto questo obiettivo, viene restituita una rappresentazione stringa
del tempo.
/* setselection.c */
#include <gtk/gtk.h>
#include <time.h>
/* Richiamata quando l'utente attiva la selezione */
void
selection_toggled (GtkWidget *widget, gint *have_selection)
{
if (GTK_TOGGLE_BUTTON(widget)->active)
{
*have_selection = gtk_selection_owner_set (widget,
GDK_SELECTION_PRIMARY,
GDK_CURRENT_TIME);
/* se il richiamo della selezione è fallito, si riporta il
bottone nello stato non premuto */
if (!*have_selection)
gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON(widget), FALSE);
}
else
{
if (*have_selection)
{
/* Prima di annullare la selezione mettendone a NULL il proprietario,
controlliamo se siamo i veri proprietari */
if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == widget->window)
gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY,
GDK_CURRENT_TIME);
*have_selection = FALSE;
}
}
}
/* Chiamata quando un'altra applicazione richiede la selezione */
gint
selection_clear (GtkWidget *widget, GdkEventSelection *event,
gint *have_selection)
{
*have_selection = FALSE;
gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON(widget), FALSE);
return TRUE;
}
/* Fornisce come selezione il tempo attuale */
void
selection_handle (GtkWidget *widget,
GtkSelectionData *selection_data,
gpointer data)
{
gchar *timestr;
time_t current_time;
current_time = time (NULL);
timestr = asctime (localtime(¤t_time));
/* Quando si restituisce una singola stringa, non occorre che finisca
con NULL. Questo verrà fatto automaticamente */
gtk_selection_data_set (selection_data, GDK_SELECTION_TYPE_STRING,
8, timestr, strlen(timestr));
}
int
main (int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *selection_button;
static int have_selection = FALSE;
gtk_init (&argc, &argv);
/* Crea la finestra di livello superiore */
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), "Event Box");
gtk_container_border_width (GTK_CONTAINER (window), 10);
gtk_signal_connect (GTK_OBJECT (window), "destroy",
GTK_SIGNAL_FUNC (gtk_exit), NULL);
/* Crea un bottone a commutazione che agisce come la selezione */
selection_button = gtk_toggle_button_new_with_label ("Claim Selection");
gtk_container_add (GTK_CONTAINER (window), selection_button);
gtk_widget_show (selection_button);
gtk_signal_connect (GTK_OBJECT(selection_button), "toggled",
GTK_SIGNAL_FUNC (selection_toggled), &have_selection);
gtk_signal_connect (GTK_OBJECT(selection_button), "selection_clear_event",
GTK_SIGNAL_FUNC (selection_clear), &have_selection);
gtk_selection_add_handler (selection_button, GDK_SELECTION_PRIMARY,
GDK_SELECTION_TYPE_STRING,
selection_handle, NULL);
gtk_widget_show (selection_button);
gtk_widget_show (window);
gtk_main ();
return 0;
}