3. Objekt-Begriff, eigene Klassen
Da GTK komplett in C implementiert ist, muß man sich auf gewisse "Konventionen" einigen, wie die Objekt-Orientierung in der Programmiersprache C abgebildet wird. Es sollte Dinge geben wie:
-
- Klassen
- Klassenvariablen
- Objekte
- Instanzvariablen
- Methoden
- Vererbung
- dynamische Bindung
Die Basisklasse GtkObject enthält vor allem Mechanismen zur Speicherverwaltung (Rerefenz-Zählung), zum Aufbau von Verbindungen zwischen Objekten (gtk_signal_connect) und zur Debug-Ausgabe.
Um zu verstehen, wie GTK über Objekte und Klassen denkt, hilft es, wenn man einfach mal einen Blick in eine typische Include-Datei wirft. Deshalb hier ein kleiner (aber typischer) Auszug aus gtk/gtkbutton.h:
#define GTK_BUTTON(obj) ....
#define GTK_IS_BUTTON(obj) ....
typedef struct _GtkButton GtkButton;
typedef struct _GtkButtonClass GtkButtonClass;
struct _GtkButton
{
GtkBin bin;
GtkWidget *child /* deprecapted field,
* use GTK_BIN (button)->child instead
*/;
guint in_button : 1;
guint button_down : 1;
guint relief : 2;
};
struct _GtkButtonClass
{
GtkBinClass parent_class;
void (* pressed) (GtkButton *button);
void (* released) (GtkButton *button);
void (* clicked) (GtkButton *button);
void (* enter) (GtkButton *button);
void (* leave) (GtkButton *button);
};
GtkType gtk_button_get_type (void);
GtkWidget* gtk_button_new (void);
GtkWidget* gtk_button_new_with_label (const gchar *label);
void gtk_button_pressed (GtkButton *button);
void gtk_button_released (GtkButton *button);
void gtk_button_clicked (GtkButton *button);
void gtk_button_enter (GtkButton *button);
void gtk_button_leave (GtkButton *button);
Die "dynamisch gebundenen Methoden" wie gtk_button_pressed(button) etc. sind nur Abkürzungen für einen länglichen Ausdruck wie:
GTK_BUTTON_CLASS(GTK_OBJECT(button)->klass)->pressed(button) etc.
Wie mache ich nun eine eigene Klasse?
Als Beispiel für eine eigene Klasse zeige ich hier das Beipiel von vorher mit einem MyButton an Stelle des GtkLabel.
button1.h:
#ifndef MY_BUTTON_H
#define MY_BUTTON_H
#include <gtk/gtk.h>
#define MY_BUTTON(obj) \
GTK_CHECK_CAST(obj, my_button_get_type(), MyButton)
#define MY_BUTTON_CLASS(klass) \
GTK_CHECK_CLASS_CAST(klass, my_button_get_type(), MyButtonClass)
#define IS_MY_BUTTON(obj) \
GTK_CHECK_TYPE(obj, my_button_get_type())
typedef struct _MyButton MyButton;
typedef struct _MyButtonClass MyButtonClass;
struct _MyButton
{
GtkButton button;
};
struct _MyButtonClass
{
GtkButtonClass parent;
};
guint my_button_get_type (void);
GtkWidget* my_button_new (const gchar *label);
#endif /* MY_BUTTON_H */
button1.c:
#include <stdio.h>
#include <gtk/gtk.h>
#include "button1.h"
static void my_button_init (MyButton *button);
guint my_button_get_type (void)
{
static GtkType my_button_type;
if (!my_button_type)
{
GtkTypeInfo type_info =
{
"MyButton",
sizeof (MyButton),
sizeof (MyButtonClass),
(GtkClassInitFunc) NULL,
(GtkObjectInitFunc) my_button_init,
/* reserved_1 */ NULL,
/* reserved_2 */ NULL,
(GtkClassInitFunc) NULL
};
my_button_type = gtk_type_unique(gtk_button_get_type(),
&type_info);
}
return my_button_type;
}
static void clicked (MyButton *button)
{
puts(GTK_LABEL(GTK_BIN(button)->child)->label);
}
static void my_button_init (MyButton *button)
{
gtk_signal_connect(GTK_OBJECT(button), "clicked",
GTK_SIGNAL_FUNC(clicked), NULL);
}
GtkWidget* my_button_new (const gchar *label)
{
MyButton *button = gtk_type_new(my_button_get_type());
gtk_container_add(GTK_CONTAINER(button), gtk_label_new(label));
return GTK_WIDGET(button);
}
int main (int argc, char *argv[])
{
GtkWindow *window;
MyButton *button;
gtk_init(&argc, &argv);
window = (GtkWindow *) gtk_window_new(GTK_WINDOW_TOPLEVEL);
button = (MyButton *) my_button_new("Hello, World!");
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(button));
gtk_widget_show_all(GTK_WIDGET(window));
gtk_signal_connect(GTK_OBJECT(window), "destroy", gtk_main_quit, NULL);
gtk_main();
return 0;
}