298 lines
8.2 KiB
OpenEdge ABL
298 lines
8.2 KiB
OpenEdge ABL
/* A demo GTK application in Pawn, using gtk-server and the "process control"
|
|
* module for Pawn. This example also illustrates the use of (communicating)
|
|
* finite state machines.
|
|
*/
|
|
#include <process>
|
|
#include <string>
|
|
|
|
enum Btn
|
|
{
|
|
Btn0, Btn1, Btn2,
|
|
Btn3, Btn4, Btn5,
|
|
Btn6, Btn7, Btn8,
|
|
Btn9, BtnDot, BtnC,
|
|
BtnCE, BtnPlus, BtnMin,
|
|
BtnMul, BtnDiv, BtnEqual,
|
|
BtnNone,
|
|
}
|
|
|
|
static entry /* GTK "entry" widget */
|
|
static numberstring[40 char]
|
|
static accum
|
|
static Btn:pending_op
|
|
|
|
adddigit(digit, bool:reset = false)
|
|
{
|
|
new command[80 char], charstr[2 char]
|
|
charstr{0} = (0 <= digit <= 9) ? digit + '0' : digit
|
|
if (reset)
|
|
numberstring[0] = EOS
|
|
strcat numberstring, charstr
|
|
strformat command, _, true, "gtk_entry_set_text %d %s", entry, numberstring
|
|
GTK(command)
|
|
}
|
|
|
|
displayresult(value)
|
|
{
|
|
new command[80 char]
|
|
valstr numberstring, value, true
|
|
strformat command, _, true, "gtk_entry_set_text %d %s", entry, numberstring
|
|
GTK(command)
|
|
}
|
|
|
|
|
|
resetentry(digit) <number:negated>
|
|
{
|
|
adddigit '-', .reset = true
|
|
adddigit digit
|
|
}
|
|
|
|
resetentry(digit) <>
|
|
{
|
|
adddigit digit, .reset = true
|
|
}
|
|
|
|
|
|
event_0() <number:zero, number:negated>
|
|
{
|
|
resetentry 0
|
|
}
|
|
|
|
event_0() <number:int, number:frac>
|
|
{
|
|
adddigit 0
|
|
}
|
|
|
|
event_1_9(Btn:idx) <number:zero, number:negated>
|
|
{
|
|
resetentry _:(idx - Btn0)
|
|
state number:int
|
|
}
|
|
|
|
event_1_9(Btn:idx) <number:int, number:frac>
|
|
{
|
|
adddigit _:(idx - Btn0)
|
|
}
|
|
|
|
event_dot() <number:zero, number:negated>
|
|
{
|
|
resetentry 0
|
|
adddigit '.'
|
|
state number:frac
|
|
}
|
|
|
|
event_dot() <number:int>
|
|
{
|
|
adddigit '.'
|
|
state number:frac
|
|
}
|
|
|
|
event_dot() <number:frac>
|
|
{
|
|
/* reject entry */
|
|
}
|
|
|
|
event_minus() <number:zero, number:negated>
|
|
{
|
|
state number:negated
|
|
resetentry 0
|
|
}
|
|
|
|
event_minus() <>
|
|
{
|
|
event_oper BtnMin /* forward to the "calc" automaton */
|
|
}
|
|
|
|
|
|
/* helper function for the calculator automaton */
|
|
performoperation()
|
|
{
|
|
/* get the other operand, perform the operation */
|
|
new val = strval(numberstring)
|
|
switch (pending_op)
|
|
{
|
|
case BtnPlus: accum += val
|
|
case BtnMin: accum -= val
|
|
case BtnMul: accum *= val
|
|
case BtnDiv: accum = (val == 0) ? 0 : accum / val
|
|
}
|
|
displayresult accum
|
|
}
|
|
|
|
event_oper(Btn:idx) <calc:idle>
|
|
{
|
|
/* save operand and operator */
|
|
accum = strval(numberstring)
|
|
pending_op = idx
|
|
|
|
state number:zero
|
|
state calc:pending
|
|
}
|
|
|
|
event_oper(Btn:idx) <calc:pending>
|
|
{
|
|
performoperation /* do the pending operation */
|
|
pending_op = idx /* save the operator for the next operation */
|
|
state number:zero
|
|
}
|
|
|
|
event_equal() <calc:idle>
|
|
{
|
|
/* ignore */
|
|
}
|
|
|
|
event_equal() <calc:pending>
|
|
{
|
|
performoperation /* do the pending operation */
|
|
state calc:idle /* reset the calculator */
|
|
state number:zero
|
|
}
|
|
|
|
|
|
event_C()
|
|
{
|
|
state calc:idle
|
|
state number:zero
|
|
resetentry 0
|
|
}
|
|
|
|
event_CE()
|
|
{
|
|
state number:zero
|
|
resetentry 0
|
|
}
|
|
|
|
|
|
main()
|
|
{
|
|
if (!procexec("gtk-server stdin") && !procexec("gtk-server.exe stdin"))
|
|
fatal "unable to launch gtk-server"
|
|
|
|
/* make a window */
|
|
GTK("gtk_init NULL NULL")
|
|
new win = GTK("gtk_window_new 0")
|
|
GTK("gtk_window_set_title %d \"Pawn calculator\"", win)
|
|
GTK("gtk_widget_set_usize %d 200 200", win)
|
|
|
|
/* add a table (align the other controls) */
|
|
new table = GTK("gtk_table_new 50 50 1")
|
|
GTK("gtk_container_add %d %d", win, table)
|
|
|
|
/* the number entry */
|
|
entry = GTK("gtk_entry_new")
|
|
GTK("gtk_table_attach_defaults %d %d 1 49 1 9", table, entry)
|
|
|
|
/* the key pad */
|
|
new buttons[Btn]
|
|
buttons[BtnDot] = GTK("gtk_button_new_with_label .")
|
|
GTK("gtk_table_attach_defaults %d %d 21 29 41 49", table, buttons[BtnDot])
|
|
buttons[Btn0] = GTK("gtk_button_new_with_label 0")
|
|
GTK("gtk_table_attach_defaults %d %d 1 19 41 49", table, buttons[Btn0])
|
|
buttons[Btn1] = GTK("gtk_button_new_with_label 1")
|
|
GTK("gtk_table_attach_defaults %d %d 1 9 31 39", table, buttons[Btn1])
|
|
buttons[Btn2] = GTK("gtk_button_new_with_label 2")
|
|
GTK("gtk_table_attach_defaults %d %d 11 19 31 39", table, buttons[Btn2])
|
|
buttons[Btn3] = GTK("gtk_button_new_with_label 3")
|
|
GTK("gtk_table_attach_defaults %d %d 21 29 31 39", table, buttons[Btn3])
|
|
buttons[Btn4] = GTK("gtk_button_new_with_label 4")
|
|
GTK("gtk_table_attach_defaults %d %d 1 9 21 29", table, buttons[Btn4])
|
|
buttons[Btn5] = GTK("gtk_button_new_with_label 5")
|
|
GTK("gtk_table_attach_defaults %d %d 11 19 21 29", table, buttons[Btn5])
|
|
buttons[Btn6] = GTK("gtk_button_new_with_label 6")
|
|
GTK("gtk_table_attach_defaults %d %d 21 29 21 29", table, buttons[Btn6])
|
|
buttons[Btn7] = GTK("gtk_button_new_with_label 7")
|
|
GTK("gtk_table_attach_defaults %d %d 1 9 11 19", table, buttons[Btn7])
|
|
buttons[Btn8] = GTK("gtk_button_new_with_label 8")
|
|
GTK("gtk_table_attach_defaults %d %d 11 19 11 19", table, buttons[Btn8])
|
|
buttons[Btn9] = GTK("gtk_button_new_with_label 9")
|
|
GTK("gtk_table_attach_defaults %d %d 21 29 11 19", table, buttons[Btn9])
|
|
|
|
buttons[BtnC] = GTK("gtk_button_new_with_label C")
|
|
GTK("gtk_table_attach_defaults %d %d 31 39 11 19", table, buttons[BtnC])
|
|
buttons[BtnCE] = GTK("gtk_button_new_with_label CE")
|
|
GTK("gtk_table_attach_defaults %d %d 41 49 11 19", table, buttons[BtnCE])
|
|
buttons[BtnPlus] = GTK("gtk_button_new_with_label +")
|
|
GTK("gtk_table_attach_defaults %d %d 31 39 21 29", table, buttons[BtnPlus])
|
|
buttons[BtnMin] = GTK("gtk_button_new_with_label -")
|
|
GTK("gtk_table_attach_defaults %d %d 41 49 21 29", table, buttons[BtnMin])
|
|
buttons[BtnMul] = GTK("gtk_button_new_with_label x")
|
|
GTK("gtk_table_attach_defaults %d %d 31 39 31 39", table, buttons[BtnMul])
|
|
buttons[BtnDiv] = GTK("gtk_button_new_with_label /")
|
|
GTK("gtk_table_attach_defaults %d %d 41 49 31 39", table, buttons[BtnDiv])
|
|
buttons[BtnEqual] = GTK("gtk_button_new_with_label =")
|
|
GTK("gtk_table_attach_defaults %d %d 31 49 41 49", table, buttons[BtnEqual])
|
|
|
|
/* initialize automata */
|
|
state number:zero
|
|
state calc:idle
|
|
|
|
/* wait for events, and dispatch them */
|
|
GTK("gtk_widget_show_all %d", win)
|
|
|
|
resetentry 0
|
|
new event
|
|
new Btn:idx
|
|
do
|
|
{
|
|
event = GTK("gtk_server_callback wait")
|
|
|
|
/* find the button matching the event, generate the event */
|
|
for (idx = Btn0; idx < BtnNone && buttons[idx] != event; idx++)
|
|
{}
|
|
switch (idx)
|
|
{
|
|
case Btn0:
|
|
event_0
|
|
case Btn1 .. Btn9:
|
|
event_1_9 idx
|
|
case BtnDot:
|
|
event_dot
|
|
case BtnMin:
|
|
event_minus
|
|
case BtnPlus, BtnMul, BtnDiv:
|
|
event_oper idx
|
|
case BtnEqual:
|
|
event_equal
|
|
case BtnC:
|
|
event_C
|
|
case BtnCE:
|
|
event_CE
|
|
}
|
|
}
|
|
while (event != win);
|
|
|
|
/* direct call, because we must not wait for a reply on this command */
|
|
procwrite "gtk_exit 0", true
|
|
}
|
|
|
|
GTK(const format[], ...)
|
|
{
|
|
new command[256 char]
|
|
switch (numargs())
|
|
{
|
|
case 1:
|
|
strpack command, format
|
|
case 2:
|
|
strformat command, _, true, format, getarg(1)
|
|
case 3:
|
|
strformat command, _, true, format, getarg(1), getarg(2)
|
|
case 4:
|
|
strformat command, _, true, format, getarg(1), getarg(2), getarg(3)
|
|
case 5:
|
|
strformat command, _, true, format, getarg(1), getarg(2), getarg(3), getarg(4)
|
|
}
|
|
procwrite command, true
|
|
|
|
new reply[30]
|
|
procread reply, .striplf=true
|
|
if (strcmp(reply, "ok") == 0)
|
|
return true
|
|
return strval(reply)
|
|
}
|
|
|
|
fatal(const message[])
|
|
{
|
|
printf "FATAL: %s\n", message
|
|
exit
|
|
}
|