Autore: Fabio Di Matteo
Ultima revisione: 13/03/2017 - 09:51
Il widget GtkTreeView è l'omologo di quello che è una tabella nel mondo reale, le celle possono contenere sia dati che altri widget.
main.c
Ecco un esempio di codice completamente commentato relativo all'immagine:
#include <gtk/gtk.h> //costanti enumerative con i nomi delle colonne enum { COL_ID , COL_COGNOME , COL_NOME, COL_CITTA, NUM_COLS } ; /*Con questa funzione prepariamo la struttura dati *che sara' contenuta nel TreeView*/ static GtkTreeModel * create_and_fill_model (void) { GtkListStore *store; //contenitore dati GtkTreeIter iter; // una specie di segnalibro che tiene il conto della posizione corrente //Creo il contenitore dei dati, specificando il tipo per ogni colonna store = gtk_list_store_new (NUM_COLS, G_TYPE_UINT, G_TYPE_STRING,G_TYPE_STRING, G_TYPE_STRING); /* Aggiungiamo una riga */ gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, COL_ID, 1, COL_COGNOME, "Di Matteo", COL_NOME, "Fabio", COL_CITTA, "Palermo", -1); /* Aggiungiamo una seconda riga */ gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, COL_ID, 2, COL_COGNOME, "Di Matteo", COL_NOME, "Gioacchino", COL_CITTA, "Palermo", -1); //prima di uscire la funzione ritorna il modello di tipo GtkTreeModel return GTK_TREE_MODEL (store); } static GtkWidget * create_view_and_model (void) { GtkCellRenderer *renderer; //la cella GtkTreeModel *model; // il modello contenente la struttura dei dati GtkWidget *view; // puntatore ad un widget generico view = gtk_tree_view_new (); //il widget generico diventa un GtkTreeView /*Disegno le colonne nel GtkTreeView (colonne, non righe! :) )*/ /* --- Colonna 1 --- */ renderer = gtk_cell_renderer_text_new (); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view), -1, "ID", renderer, "text", COL_ID, NULL); /* --- Colonna 2 --- */ renderer = gtk_cell_renderer_text_new (); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view), -1, "Cognome", renderer, "text", COL_COGNOME, NULL); /* --- Colonna 3 --- */ renderer = gtk_cell_renderer_text_new (); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view), -1, "Nome", renderer, "text", COL_NOME, NULL); /* --- Colonna 4 --- */ renderer = gtk_cell_renderer_text_new (); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view), -1, "Citta'", renderer, "text", COL_CITTA, NULL); /*Associamo al puntatore **locale** il valore di ritorno di create_and_fill_model () , * ovvero la struttura creata nella funzione precedente a questa */ model = create_and_fill_model (); /*Facciamo acuisire al TreeView "view" la struttura dati "model" . * quando distruggeremo il "view" "model" adra' distrutto con esso */ gtk_tree_view_set_model (GTK_TREE_VIEW (view), model); g_object_unref (model); return view; } int main (int argc, char **argv) { GtkWidget *window;//la finestra del programma GtkWidget *view; //il widget che diventera' il GtkTreeView gtk_init (&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); g_signal_connect (window, "delete_event", gtk_main_quit, NULL); /* dirty */ view = create_view_and_model (); gtk_container_add (GTK_CONTAINER (window), view); gtk_widget_show_all (window); gtk_main (); return 0; }
CPP = gcc OPTS = `pkg-config --cflags --libs gtk+-2.0` all: $(CPP) main.c -o main $(OPTS) clean: rm main
Adesso aggiungeremo la callback che permettera' di prelevare il contenuto di alcune celle e stamparlo sul titolo della finestra. Il codice è pressocchè lo stesso, ma con l'aggiunta della callback onRowActivated
.
#include <gtk/gtk.h> GtkWidget *window;//la finestra del programma //costanti enumerative con i nomi delle colonne enum { COL_ID , COL_COGNOME , COL_NOME, COL_CITTA, NUM_COLS } ; // Al doppioclick sul treeview eseguo la seguente callback static void onRowActivated (GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *col, //non usato gpointer user_data) { GtkTreeModel *model; GtkTreeIter iter; GtkTreeSelection *sel = gtk_tree_view_get_selection (view); g_print ("Doppio click sulla riga.\n"); model = gtk_tree_view_get_model(view); //Se volessi potrei rimuove la riga selezionata con //gtk_list_store_remove(GTK_LIST_STORE(model), &iter); if (gtk_tree_model_get_iter(model, &iter, path)) { //Prelevo alcuni campi dal modello (nome e cognome) gchar *nome, *cognome; gtk_tree_model_get(model, &iter, COL_NOME, &nome, -1); gtk_tree_model_get(model, &iter, COL_COGNOME, &cognome, -1); //Aggiorno il titolo della finestra con nome e cognome selezionati gtk_window_set_title (GTK_WINDOW(window), g_strconcat (nome, " ",cognome, NULL)); g_free(nome); g_free(cognome); }else{ //Se non ho selezionato nessun record esco senza far nulla return; } } /*Con questa funzione prepariamo la struttura dati *che sara' contenuta nel TreeView*/ static GtkTreeModel * create_and_fill_model (void) { GtkListStore *store; //contenitore dati GtkTreeIter iter; // una specie di segnalibro che tiene il conto della posizione corrente //Creo il contenitore dei dati, specificando il tipo per ogni colonna store = gtk_list_store_new (NUM_COLS, G_TYPE_UINT, G_TYPE_STRING,G_TYPE_STRING, G_TYPE_STRING); /* Aggiungiamo una riga */ gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, COL_ID, 1, COL_COGNOME, "Di Matteo", COL_NOME, "Fabio", COL_CITTA, "Palermo", -1); /* Aggiungiamo una seconda riga */ gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, COL_ID, 2, COL_COGNOME, "Di Matteo", COL_NOME, "Gioacchino", COL_CITTA, "Palermo", -1); //prima di uscire la funzione ritorna il modello di tipo GtkTreeModel return GTK_TREE_MODEL (store); } static GtkWidget * create_view_and_model (void) //Crea il vidget { GtkCellRenderer *renderer; //la cella GtkTreeModel *model; // il modello contenente la struttura dei dati GtkWidget *view; // puntatore ad un widget generico view = gtk_tree_view_new (); //il widget generico diventa un GtkTreeView /*Disegno le colonne nel GtkTreeView (colonne, non righe! :) )*/ /* --- Colonna 1 --- */ renderer = gtk_cell_renderer_text_new (); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view), -1, "ID", renderer, "text", COL_ID, NULL); /* --- Colonna 2 --- */ renderer = gtk_cell_renderer_text_new (); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view), -1, "Cognome", renderer, "text", COL_COGNOME, NULL); /* --- Colonna 3 --- */ renderer = gtk_cell_renderer_text_new (); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view), -1, "Nome", renderer, "text", COL_NOME, NULL); /* --- Colonna 4 --- */ renderer = gtk_cell_renderer_text_new (); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view), -1, "Citta'", renderer, "text", COL_CITTA, NULL); /*Associamo al puntatore **locale** il valore di ritorno di create_and_fill_model () , * ovvero la struttura creata nella funzione precedente a questa */ model = create_and_fill_model (); /*Facciamo acquisire al TreeView "view" la struttura dati "model" . * quando distruggeremo il "view" "model" adra' distrutto con esso */ gtk_tree_view_set_model (GTK_TREE_VIEW (view), model); g_object_unref (model); /*Al click su una riga attivo la callback*/ g_signal_connect(view, "row-activated", G_CALLBACK(onRowActivated), NULL); return view; } int main (int argc, char **argv) { GtkWidget *view; //il widget che diventera' il GtkTreeView gtk_init (&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); g_signal_connect (window, "delete_event", gtk_main_quit, NULL); /* dirty */ view = create_view_and_model (); gtk_container_add (GTK_CONTAINER (window), view); gtk_widget_show_all (window); gtk_main (); return 0; }
Di seguito il codice modificato per prelevare i dati al singolo click sulla griglia in Gtk+2.
Al contrario delle Gtk+3 dove basta usare la funzione gtk_tree_view_set_activate_on_single_click() in Gtk+2 dobbiamo fare un giochetto un poco piu' lungo. Dove il ruolo da protagonista
è affidato al segnale button-release-event
e alla relativa callback. Posto di seguito il codice commentato:
main.c
#include <gtk/gtk.h> GtkWidget *window; //la finestra del programma GtkTreeView* myView; //il gtk treeview GtkTreeModel* myModel; //il modello dati GtkTreeIter* myIter; //l'iteratore //costanti enumerative con i nomi delle colonne enum { COL_ID , COL_COGNOME , COL_NOME, COL_CITTA, NUM_COLS } ; // Al doppioclick sul treeview eseguo la seguente callback static void onRowActivated (GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *col, //non usato gpointer user_data) { GtkTreeModel *model; GtkTreeIter iter; GtkTreeSelection *sel = gtk_tree_view_get_selection (view); g_print ("Doppio click sulla riga.\n"); model = gtk_tree_view_get_model(view); //Se volessi potrei rimuove la riga selezionata con //gtk_list_store_remove(GTK_LIST_STORE(model), &iter); if (gtk_tree_model_get_iter(model, &iter, path)) { //Prelevo alcuni campi dal modello (nome e cognome) gchar *nome, *cognome; gtk_tree_model_get(model, &iter, COL_NOME, &nome, -1); gtk_tree_model_get(model, &iter, COL_COGNOME, &cognome, -1); //Aggiorno il titolo della finestra con nome e cognome selezionati gtk_window_set_title (GTK_WINDOW(window), g_strconcat (nome, " ",cognome, NULL)); g_free(nome); g_free(cognome); }else{ //Se non ho selezionato nessun record esco senza far nulla return; } } void onSignleButtonPressed(GtkTreeView *view, GdkEventButton *event, gpointer userdata ) { gchar* label; GtkTreePath *path=gtk_tree_model_get_path (myModel, &myIter); GtkTreeSelection * tsel = gtk_tree_view_get_selection (myView); if ( event->button == 1) { if ( gtk_tree_selection_get_selected ( tsel , &myModel , &myIter ) ) { gtk_tree_model_get(myModel, &myIter, 2, &label, -1);//prelevo il campo 2 g_print("Singolo click su: %s\n",label); } } } /*Con questa funzione prepariamo la struttura dati *che sara' contenuta nel TreeView*/ static GtkTreeModel * create_and_fill_model (void) { GtkListStore *store; //contenitore dati //Creo il contenitore dei dati, specificando il tipo per ogni colonna store = gtk_list_store_new (NUM_COLS, G_TYPE_UINT, G_TYPE_STRING,G_TYPE_STRING, G_TYPE_STRING); /* Aggiungiamo una riga */ gtk_list_store_append (store, &myIter); gtk_list_store_set (store, &myIter, COL_ID, 1, COL_COGNOME, "Di Matteo", COL_NOME, "Fabio", COL_CITTA, "Palermo", -1); /* Aggiungiamo una seconda riga */ gtk_list_store_append (store, &myIter); gtk_list_store_set (store, &myIter, COL_ID, 2, COL_COGNOME, "Di Matteo", COL_NOME, "Gioacchino", COL_CITTA, "Palermo", -1); //prima di uscire la funzione ritorna il modello di tipo GtkTreeModel return GTK_TREE_MODEL (store); } static GtkWidget * create_view_and_model (void) //Crea il vidget { GtkCellRenderer *renderer; //la cella myView = gtk_tree_view_new (); //il widget generico diventa un GtkTreeView /*Disegno le colonne nel GtkTreeView (colonne, non righe! :) )*/ /* --- Colonna 1 --- */ renderer = gtk_cell_renderer_text_new (); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (myView), -1, "ID", renderer, "text", COL_ID, NULL); /* --- Colonna 2 --- */ renderer = gtk_cell_renderer_text_new (); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (myView), -1, "Cognome", renderer, "text", COL_COGNOME, NULL); /* --- Colonna 3 --- */ renderer = gtk_cell_renderer_text_new (); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (myView), -1, "Nome", renderer, "text", COL_NOME, NULL); /* --- Colonna 4 --- */ renderer = gtk_cell_renderer_text_new (); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (myView), -1, "Citta'", renderer, "text", COL_CITTA, NULL); /*Associamo al puntatore **locale** il valore di ritorno di create_and_fill_model () , * ovvero la struttura creata nella funzione precedente a questa */ myModel = create_and_fill_model (); /*Facciamo acquisire al TreeView "view" la struttura dati "model" . * quando distruggeremo il "view" "model" adra' distrutto con esso */ gtk_tree_view_set_model (GTK_TREE_VIEW (myView), myModel); g_object_unref (myModel); return myView; } int main (int argc, char **argv) { gtk_init (&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); g_signal_connect (window, "delete_event", gtk_main_quit, NULL); /* dirty */ myView = create_view_and_model (); /*Al Doppio click su una riga attivo la callback*/ g_signal_connect(myView, "row-activated", G_CALLBACK(onRowActivated), NULL); /*Al click su una riga attivo la callback*/ g_signal_connect(myView, "button-release-event", (GCallback) onSignleButtonPressed,NULL); gtk_container_add (GTK_CONTAINER (window), myView); gtk_widget_show_all (window); gtk_main (); return 0; }
Prima di tutto occorre creare una funzione che agira sulla riga corrente.
gboolean scanListStore(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) { enum { COL_ID , COL_URL , COL_PROGRESS, NUM_COLS } ; gchar* url; gtk_tree_model_get(GTK_TREE_MODEL(model), iter, COL_URL, &url, -1); g_print("%s\n", url); }
In secondo luogo si deve lanciare una funzione che attraversera' tutto il liststore e eseguira' per la riga corrente la sopracitata funzione:
gtk_tree_model_foreach (GTK_TREE_MODEL(liststore), &scanListStore, NULL);
void traverse_list_store (GtkListStore *liststore) { GtkTreeIter iter; gboolean valid; g_return_if_fail ( liststore != NULL ); /* Get first row in list store */ valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(liststore), &iter); while (valid) { /* ... do something with that row using the iter ... */ /* (Here column 0 of the list store is of type G_TYPE_STRING) */ gtk_list_store_set(liststore, &iter, 0, "Joe", -1); /* Make iter point to the next row in the list store */ valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(liststore), &iter); } }
Trasciniamo il widget “Vista ad albero” (GtkTreeview) dentro un contenitore . Ci verra' chiesto di creare o agganciare un modello per i dati (GtkListstore) che possiamo creare e successivamente editare direttamente dalle sue proprieta'. Per il nostro esempio creeremo un modello con le seguenti colonne:
gint ID gchararray Nome gint Gradimento
La colonna “Gradimento” mostrera un valore intero sottoforma di progressbar.
Adesso andremo a creare le colonne del treeview. Per prima cosa selezioniamolo e clicchiamo su “Edit”, sulla toolbar oppure nel menu a comparsa (tasto destro del mouse). Passiamo alla scheda “Gerarchia”. Con i bottoni + e x possiamo creare o eliminare le colonne, a lato verranno visualizzate le proprieta'. Possiamo cominciare a creare le colonne ID, Nome e Gradimento.
Adesso abbiamo creato le colonne dal punto di vista grafico. Dobbiamo pero' associare ancora la colonna al nostro modello dati (il liststore). Per far questo andiamo sempre in gerarchia, clicchiamo con il tasto destro sul nome di una colonna e aggiungiamo il figlio dal menu a tendina. Una volta creato il figlio abbiniamo la proprieta' “testo” dello stesso al campo del liststore, che comparira' in un comodo menu a discesa , che deve essere dello stesso tipo.
Per creare la progressbar nel campo gradimento dobbiamo creare un figlio di tipo “Avanzamento” e asssociare un campo del liststore di tipo gint. A questo punto possiamo gia' aggiungere dei dati di prova direttamente dalle proprieta' del treeview (edit→generale). Di seguito il codice per aggiungere, eliminare e salvare righe.
main.c
#include <gtk/gtk.h> GObject *myListstore, *myTreeSel, *myTreeView; GObject *entry; GtkTreeIter iter; //Gradimento e id si autoincrementano (per semplicita') gint grad=5; gint id=1; void on_mainWindow_delete_event(GtkWidget *widget, gpointer data) { gtk_main_quit(); } // Aggiunge una riga prelevando il nome dalla entry e gli altri valori // dalle variabili "id" e "grad" void add(GtkWidget *widget, gpointer data) { enum { COL_ID , COL_NOME , COL_GRADIMENTO, NUM_COLS } ; gtk_list_store_append (GTK_LIST_STORE(myListstore), &iter); gtk_list_store_set (GTK_LIST_STORE(myListstore), &iter, COL_ID, id, COL_NOME, gtk_entry_get_text(GTK_ENTRY(entry)), COL_GRADIMENTO, grad, -1); id++; if (grad<=95)grad=grad+5; } void del(GtkWidget *widget, gpointer data) { gtk_list_store_remove(GTK_LIST_STORE(myListstore), &iter); } void save(GtkWidget *widget, gpointer data) { enum { COL_ID , COL_NOME , COL_GRADIMENTO, NUM_COLS } ; gtk_list_store_set (GTK_LIST_STORE(myListstore), &iter, COL_ID, id, COL_NOME, gtk_entry_get_text(GTK_ENTRY(entry)), COL_GRADIMENTO, grad, -1); } // Al doppioclick sul treeview eseguo la seguente callback static void onRowActivated (GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *col, //non usato gpointer user_data) { enum { COL_ID , COL_NOME , COL_GRADIMENTO, NUM_COLS } ; GtkTreeModel *model; GtkTreeSelection *sel = gtk_tree_view_get_selection (view); g_print ("Selezionata riga.\n"); model = gtk_tree_view_get_model(view); if (gtk_tree_model_get_iter(model, &iter, path)) { //Prelevoil campo nome dal liststore gchar *nome; gtk_tree_model_get(model, &iter, COL_NOME, &nome, -1); gtk_entry_set_text(GTK_ENTRY(entry),nome); g_free(nome); }else{ //Se non ho selezionato nessun record esco senza far nulla return; } } //GUI void mainWindowInit() { GError* error = NULL; gchar* glade_file = g_build_filename("gui.ui", NULL); GtkBuilder *xml; GObject *mainWindow, *btAdd, *btDel, *btSave; xml = gtk_builder_new (); if (!gtk_builder_add_from_file (xml, glade_file, &error)) { g_warning ("Couldn\'t load builder file: %s", error->message); g_error_free (error); } mainWindow=gtk_builder_get_object (xml,"mainWindow" ); btAdd=gtk_builder_get_object (xml,"btAdd" ); btDel=gtk_builder_get_object (xml,"btDel" ); btSave=gtk_builder_get_object (xml,"btSave" ); entry=gtk_builder_get_object (xml,"entry" ); myTreeView=gtk_builder_get_object(xml,"myTreeView"); myListstore=gtk_builder_get_object(xml,"myListstore"); myTreeSel=gtk_builder_get_object(xml,"myTreeSel"); g_object_unref( G_OBJECT( xml ) ); g_signal_connect (mainWindow, "destroy", G_CALLBACK (on_mainWindow_delete_event), NULL); g_signal_connect (btAdd, "clicked", G_CALLBACK (add), NULL); g_signal_connect (btDel, "clicked", G_CALLBACK (del), NULL); g_signal_connect (btSave, "clicked", G_CALLBACK (save), NULL); /*Al click su una riga attivo la callback*/ g_signal_connect(myTreeView, "row-activated", G_CALLBACK(onRowActivated), NULL); } int main (int argc, char **argv) { gtk_init (&argc, &argv); mainWindowInit(); gtk_main (); return 0; }
gui.ui
<?xml version="1.0" encoding="UTF-8"?> <!-- Generated with glade 3.20.0 --> <interface> <requires lib="gtk+" version="3.20"/> <object class="GtkListStore" id="myListstore"> <columns> <!-- column-name ID --> <column type="gint"/> <!-- column-name Nome --> <column type="gchararray"/> <!-- column-name Gradimento --> <column type="gint"/> </columns> <data> <row> <col id="0">1</col> <col id="1" translatable="yes">Fabio Di Matteo</col> <col id="2">50</col> </row> <row> <col id="0">2</col> <col id="1" translatable="yes">Tizio Rossi</col> <col id="2">10</col> </row> </data> </object> <object class="GtkWindow" id="mainWindow"> <property name="width_request">406</property> <property name="visible">True</property> <property name="can_focus">False</property> <property name="has_resize_grip">True</property> <signal name="delete-event" handler="on_mainWindow_delete_event()" swapped="no"/> <child> <object class="GtkBox"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="orientation">vertical</property> <child> <object class="GtkEntry" id="entry"> <property name="visible">True</property> <property name="can_focus">True</property> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">0</property> </packing> </child> <child> <object class="GtkScrolledWindow"> <property name="width_request">500</property> <property name="height_request">415</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="shadow_type">in</property> <child> <object class="GtkTreeView" id="myTreeView"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="model">myListstore</property> <property name="search_column">1</property> <property name="activate_on_single_click">True</property> <child internal-child="selection"> <object class="GtkTreeSelection" id="myTreeSel"/> </child> <child> <object class="GtkTreeViewColumn" id="colID"> <property name="title" translatable="yes">ID</property> <child> <object class="GtkCellRendererSpin"/> <attributes> <attribute name="text">0</attribute> </attributes> </child> </object> </child> <child> <object class="GtkTreeViewColumn" id="colNome"> <property name="title" translatable="yes">Nome</property> <child> <object class="GtkCellRendererText"/> <attributes> <attribute name="text">1</attribute> </attributes> </child> </object> </child> <child> <object class="GtkTreeViewColumn" id="colGradimento"> <property name="title" translatable="yes">Gradimento</property> <child> <object class="GtkCellRendererProgress"/> <attributes> <attribute name="value">2</attribute> </attributes> </child> </object> </child> </object> </child> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">1</property> </packing> </child> <child> <placeholder/> </child> <child> <placeholder/> </child> </object> </child> <child type="titlebar"> <object class="GtkHeaderBar"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="title">GtkTreeview e Galde</property> <property name="subtitle">Come usare il treeview con glade</property> <property name="show_close_button">True</property> <child> <object class="GtkBox"> <property name="visible">True</property> <property name="can_focus">False</property> <child> <object class="GtkButton" id="btAdd"> <property name="label">gtk-add</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <property name="use_stock">True</property> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">0</property> </packing> </child> <child> <object class="GtkButton" id="btDel"> <property name="label">gtk-delete</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <property name="use_stock">True</property> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">1</property> </packing> </child> <child> <object class="GtkButton" id="btSave"> <property name="label">gtk-save</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <property name="use_stock">True</property> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> <property name="position">2</property> </packing> </child> </object> </child> </object> </child> </object> </interface>
Il nostro wiki installa solamente cookie tecnici necessari al funzionamento della piattaforma "Dokuwiki". Niente analitics, statistiche, tracciamenti o altro.