~ gnome 2 official developers guide ->prossimo permalink
ho iniziato a leggere questo ottimo libro, http://nostarch.com/gnome.htm, che spiega in modo chiaro e dettagliato come programmare con le gtk-2.0.

ecco qua le solite note che mi sono scritto per i primi capitoli. aggiornerò qui via via che continuo a leggere.

##CHAPTER 1: GLib

memory management:
g_new()
g_new0()
      allocano memoria, gestiscono gli errori
      
memory chunks:
GMemChunk
g_mem_chunk_*
      alloca subito un x blocchi di memoria, che poi viene gestita dalla glib. la dimensione del blocco è un parametro.
      poi è possibile usare e rilasciare singoli blocchi.
      
quarks:
GQuark
g_quark_from_string(string)
g_quark_from_static_string(static string) (sconsigliato). non fa una copia della stringa.
g_quark_*
      i quark sono utili per associare numeri a stringhe, si gestiscono dinamicamente e quindi sono più flessibili degli enum.
      si usano bene solo per fare comparazioni di uguaglianza.
      
stringhe:
gchar*, gchar**
      funzioni per gestire le stringhe.

stringhe, codifiche
UCS
utf-8: importante!
      funzioni specifiche per manipolare stringhe utf-8, andrebbero usate sempre..
      validare una stringa utf-8 prima di usarla
      normalizzare due stringhe utf-8 prima di confrontarle
      conviene ottenere le key da tutte le stringhe e poi compararle con strcmp, perché altrimenti
            con le funzioni per il confronto in utf-8 ci sono overhead, tipo normalizzazione (ad ogni chiamata) e chiamate
            a sistema per allocazione di memoria.

stringhe, conversioni da codifiche a codifiche (utf-8 <=> ucs-4, utf8<=>utf-16,...)
stringhe, funzioni per i filename e per le uri utf-8

timer
GTimer
      funzioni per cronometrare il tempo
      
message logging
logga errori sulla shel. le funzioni funzionano come la printf e vanno a capo
      g_message() normale messaggio di stato, informativo
      g_warning() problemi non gravi, non causano operazioni sbagliate per addesso
      g_critical() problemi gravi
      g_error() ferma il programma      
      G_LOG_DOMAIN() macro da usare per identificare il programma nei messaggi di log.
      g_log_set_always_fatal() imposta come fatali anche messaggi non g_error()
      
      g_print() stampa sullo stdout come printf
      g_printerr() stampa sullo stderr come printf
      
      si possono impostare degli handler che lavorano sulla stringa stampata prima di dare l'output,
            con g_set_print_handler() e g_set_printerr_handler()
      g_strerror(), g_strsignal() convertono codici di errori in stringhe, interi in codici di errore.
      
debugging functions
      funzioni e macro utili per il debug delle applicazioni
      g_return_if_reached(), g_return_val_if_reached(val)
      g_return_if_fail(test), g_return_val_if_fail(test, val)            spesso usate nella libreria per controllare che i parametri siano corretti
      g_assert(test)                                          chiama g_error() se test è FALSE
      g_assert_not_reached()                                    esce con un errore
      le assertions possono essere disabilitate in fase di compilazione..
      
exception handling, cioè error reporting.
GError, funzioni per la gestione e il controllo degli errori. interessante.
      g_set_error()                                          prende un GError** e altri argomenti, serve a generare le info sull'errore
      g_propagate_error(GError** dest, GError * src)                                    propaga l'errore da una error ad un'altra.


Data structures
GString, strutture e funzioni per gestire stringhe la cui dimensione è gestita dalla GLib, crescono quando e quanto servono ogni volta.

GList, strutture e funzioni per manipolare liste collegate in avanti ed indietro
      note:
      -è più efficiente un g_list_prepend() che un g_list_append(), poiché il secondo deve scorrere tutta la lista per trovare dove inserire.
            semmai poi si può usare g_list_reverse()
      -siamo responsabili di deallocare i dati quando si cancellano elementi di una lista e quando si cancella tutta la lista.
            occhio a deallocarli una sola volta. per deallocare la lista si chiama g_list_free(list_ptr) sul primo elemento.
      -si possono ordinare le liste con g_list_sort() e g_list_sort_with_data()
      -funzioni per scorrere la lista. foreach e simili.
      
GSList, serve per le liste collegate solo in avanti.

GArray, strutture e funzioni per manipolare array che possono aumentare progressivamente di dimensione.
      -non efficiente per nulla l'inserimento in mezzo ed in cima, perché sono blocchi di celle contigue
      -efficiente l'inserimento in fondo. molto efficiente l'accesso diretto.
      -si possono ordinare come le liste.
      -deallocare gli array. posso decidere se tenere o meno i dati.
      
GTree, implementa alberi binari bilanciati.
      -creando gli alberi con g_tree_new_full() la GLib gestisce e dealloca anche la memoria dei dati dell'albero, altrimenti no.
      -occhio agli inserimenti: se inserisco una chiave che già c'è, lui dealloca la chiave che gli passo alla funzione di inserimento, pertanto
            dopo la chiamata della funzione non posso più usarla. usando invece un'altra funzione, quella che fa il replace, in caso di inserimento
            di qualcosa che già c'è mi dealloca quella vecchia. però devo stare attento, magari se il vecchio valore lo usavo da qualche parte nel
            programma va tutto ai maiali!!
      -funzioni per attraversare gli alberi, foreach e simili.
      
GHashTable, implementa hash tables.
      -per creare una hash table mi servono due funzioni, una per generare gli hash a partire dalla chiave, l'altra per valutare se due chiavi sono
            uguali
      -le hash table create con g_hash_table_new_full() si occupano anche della gestione della memoria dei dati allocati, le altre no. nel caso di
            g_hash_table_new_full() sono necessarie altre due funzioni, quella per deallocare le chiavi e quella per deallocare i valori.
      -inserimento con g_hash_table_insert() o g_hash_table_replace(), stesse problematiche descritte per gli alberi in caso di inserimento di
            una chiave già presente.
      -occhio a deallocare per bene.
      -funzioni per attraversare le hash table. foreach e simili.

Altri argomenti interessanti, non coperti dal libro
      -gestione data e ora
      -log
      -quicksort
      -GPtrArray, per array di puntatori; GByteArray, array di guint-8. interfacce semplificate
      -GStringChunk, funziona come le memory chunks, per le stringhe c (non le GString)
      -GNode, per gli alberi n-ari
      -GQueue, per le FIFO
      -shell & file utilities      
      -supporto per i threads
      -GModule, per fare programmi che supportano plugins


##CHAPTER 2: GObject
Definire una nuova classe che deriva da GObject
      -sono necessarie due strutture, la "instance structure" (ad esempio MiaClasse) e la "class structure" (ad esempio MiaClasse_Class).
      la prima contiene gli attributi della classe,
      la seconda contiene i prototipi di alcuni metodi e di tutti i segnali che possono essere emessi
      -poi sono utili alcune macro, tipo

            /* restituisce l'identificativo della classe (numero intero generato a run-time dalla libreria, uguale per tutti gli oggetti
            * della classe)
            */
            #define TYPE_MIACLASSE            (miaclasse_get_type())
            
            /* fa il cast di object in MiaClasse. utile per chiamare funzioni di MiaClasse su oggetti che ereditano da MiaClasse */
            #define MIACLASSE(object)            (G_TYPE_CHECK_INSTANCE_CAST((object), TYPE_MIACLASSE, MiaClasse))
            
            /* stessa cosa, ma fa il cast in MiaClasse_Class */
            #define MIACLASSE_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_MIACLASSE, MiaClasse_Class))
            
            /* controlla se object "is a" MiaClasse (occhio, potrebbe essere anche un oggetto che eredita da MiaClasse, quindi fare i cast!) */
            #define IS_MIACLASSE(object)      (G_TYPE_CHECK_INSTANCE_TYPE((object), TYPE_MIACLASSE))
            
            /* stessa cosa di sopra, solo che controlla se "is a" MiaClasse_Class */
            #define IS_MIACLASSE_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_MIACLASSE))
            
            /* ottiene MiaClasse_Class dall'object che "is a" MiaClass */
            #define MIACLASSE_GET_CLASS(object)      (G_TYPE_INSTANCE_GET_CLASS((object), TYPE_MIACLASSE, MiaClasse_Class))

      -e servono alcune funzioni, come quella usata nella macro
            GType miaclasse_get_type(void)
      che restituisce il GType (è un numero intero generato a run-time dalla libreria e corrisponde alla classe)
      della classe MiaClasse. ad esempio la funzione potrebbe essere così:

            GType miaclasse_get_type(void) {
             static GType miaclasse_type = 0;       //è una variabile static!
             if (!miaclasse_media_type)             //prende il valore solo la prima volta, dopo semplicemente lo restituisce
             {
             static const GTypeInfo miaclasse_info = {
             sizeof(MiaClasse_Class),            /* (guint16) class structure size */
             NULL,                              /* (GBaseInitFunc) base class initializer. utile solo quando ci sono parti della classe (non dell'oggetto) da allocare dinamicamente (quindi mai per me!) */
             NULL,                              /* (GBaseFinalizeFunc) base class finalizer. fa l'inverso di quello sopra, utile quindi solo in quei casi. */
             (GClassInitFunc)miaclasse_class_init,      /* (GClassInitFunc) class initializer. occhio, non è il costruttore dell'oggetto. This function prepares class variables, in particular, properties and signals (vedi avanti). however, the property definitions in the class initializer set up many of the defaults for the constructor.*/
             NULL,                              /* (GClassFinalizeFunc) class finalizer */
             NULL,                              /* (gconstpointer) class data */
             sizeof(MiaClasse),                  /* (guint16) instance structure size */
             4,                              /* (guint16) preallocated instances. occhio a non metterne tanti. se si mette 0, vengon allocati quando servono. */
             NULL,                              /* (GInstanceInitFunc) instance initializer. posso fare le cose che farei qui dal costruttore. */
             NULL                              /* sarà difficile usarla a meno che non si scriva il nostro sistema di oggetti al posto di GObject */
             };
             media_type = g_type_register_static(      /* questa funzione si fa una COPIA della struttura miaclasse_info, quindi poi la nostra ce la possiamo friggere */
             G_TYPE_OBJECT,                  /* parent class */
             "MiaClasse",                  /* (gchar *) type name. è una stringa */
             &miaclasse_info,                  /* (GTypeInfo *), vedi sopra */
             0);                              /* flags */
             }
             return media_type;
            }

      con questi prototipi per le funzioni:

      typedef void (*GBaseInitFunc) (gpointer g_class); typedef void (*GBaseFinalizeFunc) (gpointer g_class);
      typedef void (*GClassInitFunc) (gpointer g_class, gpointer class_data);
      typedef void (*GClassFinalizeFunc) (gpointer g_class, gpointer class_data);
      typedef void (*GInstanceInitFunc) (GTypeInstance *instance, gpointer g_class);

      in seguito fa vedere le definizioni che ci sono per l'oggetto base di tutti, GObject, e che si trovano in gobject.h.
      
      -poi si devono definire i metodi. alcune importanti note sui metodi:
            -i metodi di solito non compaiono nella "class structure" (MiaClasse_Class). I prototipi dei metodi sono definiti generalmente subito DOPO la class structure
            -i nomi dei metodi dovrebbero riflettere i nomi della classe, tipo miaclasse_xxxx()
            -il primo parametro di un metodo è sempre un oggetto (una istanza della instance structure, ad esempio di MiaClasse)
            -nei metodi pubblici è necessario controllare SEMPRE che il primo parametro sia un oggetto valido per la classe del metodo,
                  e dopo aver fatto il controllo è necessario fare il cast (con la macro MIACLASSE() definita sopra), perché l'oggetto
                  potrebbe essere di una classe derivata da MiaClasse
            -gli attributi di un oggetto sono tutti "proprietà" e ci sono dei metodi standard GTK+/GNOME per settarli
      quindi un metodo pubblico potrebbe avere questa forma

      void miaclasse_method1(MiaClasse *object) {
       MiaClasse *miaclasse;
       g_return_if_fail(IS_MIACLASSE(object));       //controllo che l'oggetto sia del tipo MiaClasse
       miaclasse = MIACLASSE(object);            //faccio il cast verso MiaClasse
      
       // do something useful
      }

      -successivamente si definiscono gli attributi, e quindi (perché così si deve fare), le proprietà.
      per definire le proprietà si usa la struttura GParamSpec e le funzioni g_param_spec_*() che operano su essa,
      in base al tipo di attributo da definire. la GParamSpec serve per descrivere un parametro (il tipo di dato, il valore di default,
      il valore massimo (se applicabile), il minimo, ecc ecc). anziché riempire a mano questa struttura, ci si può avvalere delle
      funzioni g_param_spec_*(). Le informazioni comuni a tutte le strutture GParamSpec, indipendeti quindi dal tipo di parametro
      che si va a definire, sono
            -identifier. è una stringa breve che identifica la proprietà, tipo "mia-proprieta"
            -nickname. è il nome della proprietà, tipo "mia proprietà"
            -description. è una descrizione concisa, tipo "la mia proprietà contiene questo ...".
            -Options, tipo read-write access, se la proprietà può assumere il suo valore solo nel costruttore, ecc...
      poi ci sono informazioni che variano in base al tipo di dati (ad esempio valore max, min, e default per gli interi, ecc..)
      alcuni esempi di funzoni sono g_param_spec_char(), g_param_spec_object(), g_param_spec_int(), ...
      -per capire come le proprietà vengono gestite dalla libreria, è necessario parlare di GValue.
            -GValue è un container che contiene un valore assieme al suo tipo. si può manipolare con funzioni del tipo g_value_*().
                  se ne crea uno (ad esempio che contiene gli interi, G_TYPE_INT) con (è importante inizializzare la mem. a zero)
                  GValue *value; value=g_new0(GValue, 1); g_value_init(value, G_TYPE_INT); g_value_set_int(value, 42);
                  e poi il valore lo si può recuperare (già castato) con g_value_get_int(value) dove *_int() si usa solo per
                  i valore interi, altrimenti si userebbe *_char(), ecc.. . per distruggerlo, si DEVE usare g_value_unset(value)
      -per installare le proprietà nella classe, è necessario, IN ORDINE:
            -definire un enumeration type, tipo
                  enum { PROP_MIACLASSE_0,       /* gli enum partono da zero, e lo zero NON è un indice valido per le proprietà di una classe */
                   PROP_1,                   /* indicare dei nomi, significativi, per le proprietà. il compilatore assegna valori interi progressivi */
                   PROP_2,
                   ...
                  };

            -sostituire le funzioni set_property() e get_property() che fanno parte della classe base GObject con le funzioni che definiamo noi miaclasse_set_property() e miaclasse_get_property().
            queste funzioni hanno questo prototipo (che è quello di set_property() e get_property() in GObject):
                  static void media_set_property(
                        GObject *object,
                        guint prop_id,
                        const GValue *value,
                        GParamSpec *pspec);
                  static void media_get_property(
                        GObject *object,
                        guint prop_id,
                        GValue *value,
                        GParamSpec *pspec);      


            -chiamare la funzione g_object_class_install_property(class, id, param), dove class è la "class structure", param è la GParamSpec structure della proprietà
            e id è l'identifier ottenuto dall'enumeration type. NB: id>0.
            -alla fine, si avrà una roba tipo questa

                  static void miaclasse_class_init(MiaClasse_Class *class) {
                   GParamSpec *mia_proprieta1;
                   GParamSpec *mia_proprieta2;
                        /* e così via per le altre proprietà */

                   GObjectClass *g_object_class;

                   /* prende la structure class del GObject a partire da MiaClasse_Class */
                   g_object_class = G_OBJECT_CLASS(class);

                   /* create GParamSpec descriptions for properties */
                  
                   mia_proprieta1 = g_param_spec_uint("prop-1", /* identifier */
                   "proprietà 1", /* nickname */
                   "descrizione 1", /* description */
                   0, /* minimum */
                   UINT_MAX, /* maximum */
                   0, /* default */
                   G_PARAM_READWRITE); /* flags */
                   mia_proprieta2 = g_param_spec_boolean("prop-2",
                   "flag?",
                   "descrizione",
                   FALSE,
                   G_PARAM_READWRITE);

                   /* sostituisco i metodi dell'oggetto base con quelli definiti da me */
                   g_object_class->set_property = miaclasse_set_property;
                   g_object_class->get_property = miaclasse_get_property;
                  
                   /* installo le proprietà */
                   g_object_class_install_property(g_object_class,
                   PROP_1,
                   mia_proprieta_1);
                   g_object_class_install_property(g_object_class,
                   PROP_2,
                   mia_proprieta_2);
                  }

            -adesso non rimane che scrivere le funzioni miaclasse_set_property() e miaclasse_get_property()
                  miaclasse_set_property() deve: determinare la proprietà da impostare, estrarre il nuovo valore dal GValue container
                  ricevuto come parametro, ed infine settare il campo nella struct MiaClasse.
                  ecco qua gli esempi:
                  static void miaclasse_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
                  {
                   MiaClasse *miaclasse;
                   guint mia_proprieta_1_nuova;
                   gboolean mia_proprieta_2_nuova;
                   miaclasse = MIACLASSE(object);       //casto a MiaClasse
                   switch(prop_id)
                   {
                   case PROP_1:
                   mia_proprieta_1_nuova = g_value_get_uint(value);
                   if (miaclasse->mia_proprieta_1 != mia_proprieta_1_nuova)
                   {
                   miaclasse->mia_proprieta_1 = mia_proprieta_1_nuova;
                   }
                   break;
                   case PROP_2:
                   mia_proprieta_2_nuova = g_value_get_boolean(value);
                   if (miaclasse->mia_proprieta_2 != mia_proprieta_2_nuova)
                   {
                   miaclasse->mia_proprieta_2 = mia_proprieta_2_nuova;
                   }
                   break;
                   /* e così per tutte le altre proprietà */
                   default            //errore: non ho gestito tutte le proprietà, qui non dovrebbe mai arrivarci
                   G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
                   break;
                   }
                  }

                  static void miaclasse_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
                   {
                   MiaClasse *miaclasse;
                   miaclasse = MIACLASSE(object);       //casto a MiaClasse
                   switch(prop_id)
                   {
                   case PROP_1:
                   g_value_set_uint(value, miaclasse->mia_proprieta_1);
                   break;
                   case PROP_2:
                   g_value_set_boolean(value, miaclasse->mia_proprieta_2);
                   break;
                   /* e così per tutte le altre proprietà */
                   default:             //errore: non ho gestito tutte le proprietà, qui non dovrebbe mai arrivarci
                   G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);       
                   break;
                   }
                  }

            -questa metodologia per settare e leggere proprietà è utile perché così, ad esempio, posso in un unico posto impostare
            che vengano generati dei segnali al cambiamento di una proprietà, ho un'interfaccia unica per gestire le proprietà, ecc..
            
      QUINDI, ora ho creato una classe.
      per istanziare un'oggetto di quella classe, uso
      MiaClasse *miaclasse;
/* istanzia l'oggetto inizializzando le proprietà a quelle default */
      miaclasse = g_object_new(TYPE_MIACLASSE, NULL);

/* oppure, per istanziare l'oggetto e impostare anche qualche proprietà */
      miaclasse = g_object_new(TYPE_MIACLASSE,
"prop-1", 42,
"prop-2", FALSE,
NULL);       //finisce SEMPRE con NULL

      oppure mi definisco una funzione miaclasse_new() che fa quelle istruzioni e rende il puntatore all'oggetto.
      per impostare una proprietà posso usare una di queste funzioni
      g_object_set(miaclasse, "prop-2", TRUE, "prop-1", 37, NULL);       //imposta un insieme di proprietà. finisce con NULL
      g_object_get(miaclasse, "prop-2", &is_prop_2, "prop-1", &prop_1, NULL); //legge le proprietà, con boolean is_prop_2; int prop_1; finisce con NULL
      g_object_get_property(G_OBJECT(miaclasse), "prop-1", gvalue_ptr)       //imposta una sola proprietà da un GValue. deve essere stato già inizializzato; GValue * gvalue_ptr; gvalue_ptr=g_new0(GValue, 1); g_value_init(gvalue_ptr, G_TYPE_UINT); g_value_set_uint(gvalue_ptr, 34)
      g_object_get_property(G_OBJECT(miaclasse), "prop-1", gvalue_ptr)       //legge una sola proprietà in un GValue. deve essere stato già inizializzato; GValue * gvalue_ptr; gvalue_ptr=g_new0(GValue, 1); g_value_init(gvalue_ptr, G_TYPE_UINT);
      
      -riferimenti agli oggetti:
      quando si istanzia un oggetto, si ottiene un riferimento a quell'oggetto. questo viene memorizzato dalla libreria in un contatore. per ottenere un altro
      riferimento, si usa la funzione nuovo_riferimento=g_object_ref(vecchio_riferimento). tutti i riferimenti sono uguali tra loro.
      via via che si distruggono gli oggetti con la funzione g_object_unref(riferimento), il counter viene decrementato. quando va a zero significa che non ci sono più riferimenti e l'oggetto
      viene effettivamente distrutto.
      -si possono creare anche riferimenti "deboli": questi non incrementano il counter, e quando il counter va a zero, questi vengono settati a
      NULL dalla libreria. per creare un riferimento debole si usa g_object_add_weak_pointer(object, &rif_debole_nuovo); rif_debole_nuovo=object;
      con g_object_remove_weak_pointer(object, &rif_debole_nuovo) si rimuove il riferimento debole.
      
      -g_object_weak_*ref(). questa funzione (non documentata nel libro) permette di chiamare una funzione quando un oggetto viene distrutto (il counter va a zero).
      
      
      
sono arrivato a 2.6 Signals
ste's home page
da qualche parte un giorno / dove non si saprà
dove non l'aspettate / il che ritornerà