/*
* Calculator.c
*
* Example showing different widgets in use.
*
*/
#include "gtk/gtk.h"
#include <math.h>
#include <ctype.h>
static float num1 = 0;
static char lastChar = (char) 0;
static char prevCmd = (char) 0;
#define BUF_SIZE 88
/*
* --- data structure to keep track of the calculator buttons.
*/
typedef struct {
char *szLabel; /* --- Label display on button --- */
int row; /* --- Row to place the button --- */
int col; /* --- Column to place the button --- */
GtkWidget *widget; /* --- Handle to the button --- */
} typCalculatorButton;
/*
* --- This is the button list. Each button is documented here so
* we can access it.
*/
typCalculatorButton buttonList [] = {
{"C", 1, 0, NULL}, /* --- Clear --- */
{"CE", 1, 1, NULL}, /* --- Clear --- */
{"/", 1, 3, NULL}, /* --- Division --- */
{"7", 2, 0, NULL}, /* --- Digit --- */
{"8", 2, 1, NULL}, /* --- Digit --- */
{"9", 2, 2, NULL}, /* --- Digit --- */
{"*", 2, 3, NULL}, /* --- Multiplication --- */
{"%", 2, 4, NULL}, /* --- Percent --- */
{"4", 3, 0, NULL}, /* --- Digit --- */
{"5", 3, 1, NULL}, /* --- Digit --- */
{"6", 3, 2, NULL}, /* --- Digit --- */
{"-", 3, 3, NULL}, /* --- Subtraction --- */
{"1/x", 3, 4, NULL}, /* --- 1/x --- */
{"1", 4, 0, NULL}, /* --- Digit --- */
{"2", 4, 1, NULL}, /* --- Digit --- */
{"3", 4, 2, NULL}, /* --- Digit --- */
{"+", 4, 3, NULL}, /* --- Addition --- */
{"sqrt",4, 4, NULL}, /* --- Square root --- */
{"+/-", 5, 0, NULL}, /* --- Negate value --- */
{"0", 5, 1, NULL}, /* --- zero --- */
{".", 5, 2, NULL}, /* --- Decimal --- */
{"=", 5, 3, NULL}, /* --- Equals/total --- */
{"x^2", 5, 4, NULL}, /* --- Squared --- */
};
/*
* --- Number of buttons in the data structure.
*/
int nButtons = sizeof (buttonList) /
sizeof (typCalculatorButton);
/* --- This is the LCD panel - the results --- */
GtkWidget *label;
/*
* CloseAppWindow
*
* The window is closing down, end the gtk loop
*/
gint CloseAppWindow (GtkWidget *widget, gpointer data)
{
gtk_main_quit ();
return (FALSE);
}
/*
* TrimTrailingZeros
*
* Get rid of trailing zeros
* Takes the string and removes the trailing zeros.
*/
void TrimTrailingZeros (char *szDigits)
{
int nIndex;
int bDecimal = FALSE;
int nPos = -1;
/* --- Loop through the string. --- */
for (nIndex = 0; nIndex < strlen (szDigits); nIndex++) {
/* --- Is this a decimal? --- */
if (szDigits[nIndex] == '.') {
bDecimal = TRUE;
}
/* --- If we're on the right side of the decimal... --- */
if (bDecimal) {
/* --- A zero? Hmm... from this point on? --- */
if (szDigits[nIndex] == '0') {
/* --- If we don't have a point yet... --- */
if (nPos < 0) {
/* --- Save this as a point. --- */
nPos = nIndex;
}
} else {
/* --- Clear it. Bad point. --- */
nPos = -1;
}
}
}
/* --- Truncate the field. --- */
if (nPos > 0) {
szDigits[nPos] = (char) 0;
}
}
/*
* TrimLeadingZeros
*
* Trim the leading zeros.
*
* Converts numbers like "0000012" to "12"
*/
void TrimLeadingZeros (char *szDigits)
{
int nPos;
if (szDigits == NULL) return;
/* --- While we have a combination a digit in front --- */
for (nPos = 0; (szDigits[nPos] && szDigits[nPos] == '0'); nPos++) {
/* --- If the digit is a zero and next char is a digit --- */
if (isdigit (szDigits[nPos+1])) {
/* --- Blank the field. --- */
szDigits[nPos] = ' ';
}
}
}
/*
* Command
*
* Returns true if the character is a two digit command.
*/
int Command (char ch)
{
switch (ch) {
case '+':
case '-':
case '/':
case '*':
case '=':
return (TRUE);
}
return (FALSE);
}
/*
* FloatingPointChar
*
* Returns true if the character is any of [0123456789.]
*/
int FloatingPointChar (char ch)
{
return (isdigit (ch) || ch == '.');
}
/*
* key_press
*
* Handle the button "key_press" event.
*
* Function looks for the keystroke in the calculator
* data structure and (if a match is found) presses the
* button that matches the keystroke for the user. It
* keeps our code small since we only have to handle the
* button_clicked events.
*/
void key_press (GtkWidget *widget,
GdkEventKey *event,
gpointer data)
{
int nIndex;
/* --- Search through the buttons --- */
for (nIndex = 0; nIndex < nButtons; nIndex++) {
/* --- If the keystroke is the first character of a button AND --- */
/* --- the button label length is one. --- */
if (event->keyval == buttonList[nIndex].szLabel[0] &&
buttonList[nIndex].szLabel[1] == (char) 0) {
/* --- Set focus to that button --- */
gtk_widget_grab_focus (buttonList[nIndex].widget);
/* --- Make like the button was clicked to do processing. --- */
gtk_button_clicked (GTK_BUTTON (buttonList[nIndex].widget));
return;
}
}
}
/*
* HandleDigit
*
* Digit button was pressed, deal with it. How it
* is dealt with depends on the situation.
*/
void HandleDigit (char *str, char ch)
{
char *labelText;
char buffer[BUF_SIZE];
int len;
/* --- And they just did a command --- */
if (Command (lastChar)) {
/* --- Clear the digit field --- */
gtk_label_set (GTK_LABEL (label), "");
/* --- If they did a computation --- */
if (lastChar == '=') {
/* --- Clear out the command --- */
lastChar = (char) 0;
prevCmd = (char) 0;
}
}
/* --- Get the buffer in the led --- */
gtk_label_get (GTK_LABEL (label), &labelText);
strcpy (buffer, labelText);
/* --- Add the new character on it. --- */
len = strlen (buffer);
buffer[len] = (gchar) ch;
buffer[len+1] = (gchar) 0;
/* --- Trim leading zeros. --- */
TrimLeadingZeros (buffer);
/* --- Add digit to field. --- */
gtk_label_set (GTK_LABEL (label), (char *) buffer);
}
/*
* MaybeUnary
*
* str
*
* Check to see if the user hit a unary operator button -
* like %, sqrt, 1/x, etc that should be dealt with NOW
* not later.
*/
void MaybeUnaryOperation (char *str)
{
char *labelText;
char buffer[BUF_SIZE];
float num2;
/* --- Get number in the field. --- */
gtk_label_get (GTK_LABEL (label), &labelText);
num2 = atof (labelText);
/* --- Percentage? --- */
if (strcmp (str, "%") == 0) {
num2 = num2 / 100;
/* --- Trying for 1/x? --- */
} else if (strcmp (str, "1/x") == 0) {
/* --- Can't divide by zero. --- */
if (num2 == 0) {
/*Error (); */
return;
}
num2 = 1 / num2;
/* --- Calculate sqrt --- */
} else if (strcmp (str, "sqrt") == 0) {
num2 = sqrt ((double) num2);
/* --- Calculate square --- */
} else if (strcmp (str, "x^2") == 0) {
num2 = num2 * num2;
}
/* --- Put the number back. --- */
sprintf (buffer, "%f", (float) num2);
TrimTrailingZeros (buffer);
TrimLeadingZeros (buffer);
gtk_label_set (GTK_LABEL (label), buffer);
}
void HandleBinaryOperation ()
{
char buffer[BUF_SIZE];
char *labelText;
float num2;
/* --- Get number in the field. --- */
gtk_label_get (GTK_LABEL (label), &labelText);
num2 = atof (labelText);
/* --- Calculate based on previous command. --- */
switch (prevCmd) {
case '+':
num1 = num