Neuerdings ist es in Mode gekommen, die Gestaltung von GEM-Dialogboxen an die des Apple Macintosh anzulehnen, was sicherlich keinesfalls verkehrt ist. Wenn man sich jedoch ansieht, was manche Programmierer sich dabei für eine Mühe geben, um zum Beispiel solche Kleinigkeiten wie Checkboxen unter GEM zu realisieren, da fragt man sich, ob sich diese Leute die Fähigkeiten des GEM überhaupt genauer angesehen haben.
Die sogenannte Tatsache (?), daß GEM Checkboxen nicht bzw. nur unzureichend unterstützt, ist offenbar den meisten Programmierern Grund genug, die Finger davon zu lassen. Die anderen, die solche Sachen wie Checkboxen und beispielsweise runde Radiobuttons (wie vom Macintosh bekannt) benutzen wollen, machen sich andererseits einen Haufen (unnötige) Arbeit, indem sie benutzerdefinierte Objekte anlegen und das benötigte Objekt bis in die letzte Einzelheit mittels VDI-Befehlen zeichnen. Bei runden Buttons ist das ja noch einzusehen, da GEM diese von sich aus nicht anbietet, bei Checkboxen jedoch absolut nicht.
Checkboxen sind unter GEM normale Objekte vom Typ G_BOX, die mit dem Status CROSSED versehen sind. Sie gelten nur aus dem Grund als unbrauchbar, weil das Kreuz von GEM grundsätzlich in weißer Farbe gezeichnet wird - offensichtlich handelt es sich dabei um einen Fehler, der von Atari bis heute nicht behoben wurde. Dieses weiße Kreuz ist insofern unbrauchbar, da man für gewöhnlich einen weißen Hintergrund für Buttons benutzt - und das ist in etwa so wie die ostfriesische Nationalflagge: weißer Adler auf weißem Grund.
Nichtsdestoweniger läßt sich dieses Problem jedoch mit einem kleinen und völlig simplen Trick umgehen, wie das vorliegende Listing zeigt. Das Programm öffnet eine Dialogbox mit einer Checkbox und einem Exit-Button. Die Checkbox kann beliebig oft angeklickt werden und ändert jedesmal ihren Status von CROSSED auf NORMAL bzw. umgekehrt.
Wie funktioniert das Ganze nun? Die Antwort ist verblüffend einfach: Man nimmt eine gewöhnliche G_BOX und versieht sie mit dem Status CROSSED. Wie bereits geschildert, erhält man dadurch ein weißes und damit nicht sichtbares Kreuz auf einem weißen Hintergrund. Deshalb muß man die G_BOX jetzt noch mit der Hintergrundfarbe Schwarz versehen, und man bekommt ein weißes Kreuz auf einem schwarzen Hintergrund, was zwar immer noch nicht das gewünschte Ergebnis darstellt, aber schon wesentlich näher am Ziel ist; immerhin kann man jetzt schon das Kreuz vom Hintergrund unterscheiden.
Der eigentliche Trick kommt erst jetzt: Entweder belegt man im Resource Construction Set das Objekt mit dem Status SELECTED vor, oder man selektiert die Checkbox im Programm vor der Benutzung einmal; dies wird auch so im Beispielprogramm gemacht. Um zu verstehen, warum man dadurch plötzlich eine „richtige“ Checkbox erhält, muß man wissen, wie GEM den Zustand SELECTED bearbeitet: Es wird nämlich das gesamte Objekt, in diesem Fall eine G_BOX mit dem Status CROSSED und einem schwarzen Hintergrund, komplett invertiert. Dadurch erhält man dann nichts anderes als eine G BOX mit einem weißen Hintergrund und einem schwarzen Kreuz - also eine „richtige“ Checkbox!
Wenn der Button angeklickt wird, ist nun nichts anderes mehr zu tun, als den Zustand CROSSED zu löschen bzw. bei erneutem Anklicken wieder zu setzen und anschließend die Checkbox neu zu zeichnen. - Voilà!
Abschließend noch ein Wort zum Thema Radiobuttons. Wie oben bereits erwähnt, benutzen viele Programme mittlerweile die Macintosh-typischen runden Radiobuttons, die sicherlich auch optisch eindrucksvoll sind. Wer sich jedoch nicht soviel Mühe machen will, dem steht auch hier unter GEM eine wesentlich einfachere Möglichkeit offen, die sich meiner Meinung nach optisch auch besser ins GEM eingliedert, da runde Objekte in GEM für gewöhnlich nicht vertreten sind.
Man nimmt dazu einfach wieder eine G_BOX und versieht sie mit dem Status OUTLINED. Bei Anklicken muß die Box nur noch selektiert werden, was GEM ja bekanntlich automatisch macht, und schon hat man einen Radiobutton, der zwar eckig statt rund ist, sich aber ansonsten weder in Aussehen noch in Funktionsweise von den runden Radiobuttons unterscheidet. Noch dazu hebt er sich von den oben eingeführten Checkboxen sehr gut ab und ist lange nicht so aufwendig zu programmieren wie ein runder Radiobutton, für den wieder einmal ein benutzerdefiniertes Objekt angelegt werden muß. Die Abbildung zeigt, wie eine solche Dialogbox aussehen könnte; die Unterschiede zwischen den beiden Button-Typen sind deutlich zu erkennen.
Mit anderen Worten: Auch mit „normaler“ GEM-Programmierung läßt sich immer noch sehr viel mehr aus GEM herausholen, als den meisten Leuten bisher bekannt zu sein scheint.
/***********************************************/
/* Es gibt sie doch! - Checkboxen unter GEM */
/* Autor: Uwe Hax */
/* (C) 1991 MAXON Computer */
/***********************************************/
#include <aes.h>
#include <portab.h>
#include <string.h>
#include <stdlib.h>
/* Defines für Zugriff auf die Dialgobox */
#define DIALOG 0
#define CHECKBOX 1
#de£ine OK 3
#define STATUS 5
/* Resource-Definitionen für Dialogbox */
OBJECT test_dialog[] = {
-1, 1, 5, G_BOX, NONE, OUTLINED, 0x21100L,
0,0, 176,128,
2, -1, -1, G_BOX, TOUCHEXIT, CROSSED,
0xFF1171L, 24,48, 16,16,
3, -1, -1, G_STRING, NONE, NORMAL,
(LONG)"GEM-Checkbox", 56,48, 96,16,
4, -1, -1, G_BUTTON, 0x7, NORMAL, (LONG)"Ok",
56,96, 64,16,
5, -1, -1, G_STRING, NONE, NORMAL,
(LONG)"Test-Dialog", 48,16, 96,16,
0, -1, -1, G_STRING, LASTOB, NORMAL,
(LONG)"(Status: ein)", 56,64, 96,16
};
VOID main(VOID)
{
WORD x, y, w, h;
WORD objc_x,objc_y;
WORD button;
WORD dummy;
/* Programm anmelden */
appl_init();
graf_mouse(M_OFF,0L);
graf_mouse(ARROW,0L);
/* Trick 17: Box-Inhalt invertieren */
test_dialog[CHECKBOX].ob_state |= SELECTED;
/* Dialogbox öffnen */
form_center(test_dialog,&x,&y,&w,&h);
form_dial(FMD_START,x,y,w,h,x,y,w,h);
objc_draw(test_dialog,ROOT,MAX_DEPTH,x,y,w,h);
graf_mouse(M_ON,0L);
/* Dialog auswerten */
do
{
button=form_do(test_dialog,0) & 0x7fff;
/* Checkbox angeklickt? */
if (button==CHECKBOX)
{
/* Kreuz ein- und ausschalten */
if (test_dialog[CHECKBOX].ob_state & CROSSED)
{
test_dialog[CHECKBOX].ob_state &= ~CROSSED;
strncpy(&test_dialog[STATUS].ob_spec.free_string[9],"aus",3);
}
else
{
test_dialog[CHECKBOX].ob_state |= CROSSED;
strncpy(&test_dialog[STATUS].ob_spec.free_string[9],"ein",3);
}
/* Checkbox neu zeichnen */
graf_mouse(M_OFF,0L);
objc_offset(test_dialog,CHECKBOX,&objc_x,&objc_y);
objc_draw(test_dialog,ROOT,MAX_DEPTH,
objc_x,objc_y,
test_dialog[CHECKBOX].ob_width,
test_dialog[CHECKBOX].ob_height);
graf_mouse(M_ON,0L);
/* Status neu zeichnen */
objc_offset(test_dialog,STATUS,&objc_x,&objc_y);
objc_draw(test_dialog,ROOT,MAX_DEPTH,
objc_x,objc_y,
test_dialog[STATUS].ob_width,
test_dialog[STATUS].ob_height);
/* Warten, bis Mausclick-Ende */
evnt_button(1,1,0,&dummy,&dummy,&dummy,&dummy);
}
}
while (test_dialog[button].ob_flags & TOUCHEXIT);
/* Dialogbox entfernen und Programm beenden */
form_dial(FMD_FINISH,x,y,w,h,x,y,w,h);
appl_exit();
exit(0);
}