Code:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <gtk/gtk.h>
/*
* Diese Funktion wird als Callback fuer das delete-Event des Hauptfensters
* gesetzt, d.h. sie wird aufgerufen, wenn das Fenster geschlossen werden
* soll. Hier kann man dann noch weitere Abfragen (z.B. ein Dialog-Fenster, das
* abfragt, ob man etwas speichern will) reinklatschen.
* Wenn die Funktion TRUE zurueckgibt, wird das Fenster _nicht_ geschlossen,
* bei FALSE wird das destroy-Event des Fensters aufgerufen (s. unten).
*/
static gboolean delete (GtkWidget *widget, GdkEvent *event, gpointer data)
{
return FALSE;
}
/*
* Callback fuer das destroy-Event des Hauptfensters. Soll das Fenster
* schliessen, in diesem Falle beenden wir einfach die Mainloop von GTK.
*/
static gboolean destroy (GtkWidget *widget, gpointer data)
{
gtk_main_quit();
}
/* Da ich diese Variable in mehreren Funktionen brauche und zu faul bin, mir
'nen vernuenftigen Programmierstil zuzulegen, ist sie global. Das ist
uebrigens das Textfeld des Rechners.*/
GtkWidget *text;
/*
* Das hier ist der Callback fuer die Buttons (ja, ein Callback fuer 18
* Buttons).
* Die Funktion kriegt als letztes Argument die Beschriftung des Buttons
* uebergeben (s. oben), die jagen wir durch einen Switch, um festzustellen,
* welcher Button geklickt wurde.
* Das Toolkit tut hier nichts weiter interessantes, ausser das Textfeld zu
* aendern, daher ist das nicht ausfuehrlicher dokumentiert.
*/
static void button_cb (GtkWidget *button, gpointer data)
{
static double num = 0 ;
static char operation = 0 ;
static gboolean comma = FALSE;
static gboolean deltext = TRUE;
double tmpnum;
char b = *((char *) data);
switch (b)
{
case '1' :
case '2' :
case '3' :
case '4' :
case '5' :
case '6' :
case '7' :
case '8' :
case '9' :
case '0' :
if (deltext)
{
gtk_entry_set_text(GTK_ENTRY(text), (char *) data);
deltext = FALSE;
}
else
gtk_entry_append_text(GTK_ENTRY(text), (char *) data);
break ;
case 'C' :
gtk_entry_set_text(GTK_ENTRY(text), "0" );
num = 0 ;
operation = 0 ;
comma = FALSE;
deltext = TRUE;
break ;
case '#' :
if (gtk_entry_get_text(GTK_ENTRY(text))[0 ] == '-' )
gtk_entry_set_text(GTK_ENTRY(text),
gtk_entry_get_text(GTK_ENTRY(text)) + 1 );
else
gtk_entry_prepend_text(GTK_ENTRY(text), "-" );
break ;
case '+' :
case '-' :
case '*' :
case '/' :
case '=' :
tmpnum = atof(gtk_entry_get_text(GTK_ENTRY(text)));
switch (operation)
{
case '+' :
num += tmpnum;
break ;
case '-' :
num -= tmpnum;
break ;
case '*' :
num *= tmpnum;
break ;
case '/' :
num /= tmpnum;
break ;
default :
num = tmpnum;
}
if (b == '=' )
operation = 0 ;
else
operation = b;
comma = FALSE;
/* neue Zahl ins Textfeld schreiben.
Danke an Luki fuer den Hinweis auf asprintf. */
char *format;
if (floor(num) == num)
format = "%.0f" ;
else
format = "%f" ;
char *newtext;
asprintf(&newtext, format, num);
gtk_entry_set_text(GTK_ENTRY(text), newtext);
free(newtext);
deltext = TRUE;
break ;
case '.' :
if (deltext)
{
gtk_entry_set_text(GTK_ENTRY(text), "0." );
deltext = FALSE;
}
else
if (!comma)
{
comma = TRUE;
gtk_entry_append_text(GTK_ENTRY(text), "." );
}
break ;
}
}
/*
* Wir bauen uns ein Fenster.
* Diese Funktion erstellt das Fenster des Rechners und die zugehoerigen
* Buttons und das Eingabefeld, haengt die Callbacks an die Buttons und packt
* das Ganze in eine Layout-Tabelle.
*/
static GtkWidget *calc_window_new ()
{
GtkWidget *window, *table;
GtkWidget *buttons[5 ][4 ];
char *button_labels[5 ][4 ] = {{ "C" , "#" , "/" , "*" } ,
{ "7" , "8" , "9" , "-" } ,
{ "4" , "5" , "6" , "+" } ,
{ "1" , "2" , "3" , "=" } ,
{ "0" , "." , 0 , 0 }} ;
/* Fenster, Layouttabelle und Textfeld erstellen, Textfeld read-only
* und rechtsbuendig machen, Spacing fuer die Tabelle setzen */
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
table = gtk_table_new(6 , 4 , TRUE);
gtk_table_set_row_spacings(GTK_TABLE(table), 6 );
gtk_table_set_col_spacings(GTK_TABLE(table), 8 );
text = gtk_entry_new();
gtk_entry_set_editable(GTK_ENTRY(text), FALSE);
gtk_entry_set_alignment(GTK_ENTRY(text), 1.0 f);
gtk_entry_set_text(GTK_ENTRY(text), "0" );
gtk_widget_show(text);
/* Buttons bauen */
int i, j;
for (i = 0 ; i < 5 ; ++i)
{
for (j = 0 ; j < (i < 4 ? 4 : 2 ); ++j)
{
/* wir wollen schoene Unicode-Zeichen auf den Buttons
* (auf meinem Rechner mit UTF-8-encodeten Files geht das, falls
* euer Compiler streikt, ersetzt die Strings unten durch was
* anderes)
* Die Unicode-Zeichen stehen nicht direkt im Labels-Array, weil
* sie dann nicht vom Switch in button_cb() abgefragt werden
* koennten.
*/
char *label;
switch (button_labels[i][j][0 ])
{
case '#' :
label = "±" ;
break ;
case '/' :
label = "÷" ;
break ;
case '*' :
label = "×" ;
break ;
default :
label = button_labels[i][j];
}
buttons[i][j] = gtk_button_new_with_label(label);
gtk_widget_show(buttons[i][j]);
g_signal_connect(G_OBJECT(buttons[i][j]), "clicked" ,
G_CALLBACK(button_cb),
(gpointer) button_labels[i][j]);
}
}
/* Widgets in die Layout-Tabelle packen */
gtk_table_attach_defaults(GTK_TABLE(table), text, 0 , 4 , 0 , 1 );
for (i = 0 ; i < 4 ; ++i)
{
for (j = 0 ; j < (i < 3 ? 4 : 3 ); ++j)
gtk_table_attach_defaults(GTK_TABLE(table), buttons[i][j], j,
j + 1 , i + 1 , i + 2 );
}
/* Sonderbehandlung fuer die Buttons 0, , und = */
gtk_table_attach_defaults(GTK_TABLE(table), buttons[4 ][0 ], 0 , 2 , 5 , 6 );
gtk_table_attach_defaults(GTK_TABLE(table), buttons[4 ][1 ], 2 , 3 , 5 , 6 );
gtk_table_attach_defaults(GTK_TABLE(table), buttons[3 ][3 ], 3 , 4 , 4 , 6 );
/* Tabelle in das Fenster setzen, Abstand zum Rand setzen, anzeigen */
gtk_container_add(GTK_CONTAINER(window), table);
gtk_container_set_border_width(GTK_CONTAINER(window), 10 );
gtk_widget_show(table);
return window;
}
/*
* main initialisiert GTK, erstellt das Fenster, setzt die Callbacks und
* startet die GTK-Mainloop.
*/
int main (int argc, char *argv[])
{
GtkWidget *window;
gtk_init(&argc, &argv);
window = calc_window_new();
gtk_window_set_title(GTK_WINDOW(window), "mq kotet C" );
/* Callbacks fuer die Events delete und destry (s. oben) setzen */
g_signal_connect(G_OBJECT(window), "delete_event" , G_CALLBACK(delete),
NULL );
g_signal_connect(G_OBJECT(window), "destroy" , G_CALLBACK(destroy), NULL );
/* anzeigen uns los */
gtk_widget_show(window);
gtk_main();
return 0 ;
}