2013-09-19 13:06:46 +07:00

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
}