File: calculator.lua

package info (click to toggle)
lua-gtk 0.9%2B20100528-2
  • links: PTS, VCS
  • area: main
  • in suites: squeeze
  • size: 2,176 kB
  • ctags: 1,934
  • sloc: ansic: 9,571; sh: 373; makefile: 241
file content (138 lines) | stat: -rwxr-xr-x 3,203 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#! /usr/bin/env lua
-- vim:sw=4:sts=4
--
-- Simple calculator demo.  Evaluates input as Lua expression.
-- by Wolfgang Oertl 2007.  The buttons are quite useless as you'd type
-- the numbers anyway, but this should show off some features, so...
--
-- Possible improvements: allow assignments to variables.
--

require "gtk"

local CALC = {}
CALC.__index = CALC

--
-- Calculate the expression by evaluating it as Lua expression.
-- Returns the result (a value as string) or nil and an error message.
--
function calculate(s)
    local chunk, msg = loadstring("return " .. s, s)
    if not chunk then
	return nil, string.match(msg, ":%d: (.*)")
    end

    -- The global environment of the user defined function is set to "math", so
    -- all functions of the math library (e.g. abs, asin, ceil, deg, exp,
    -- floor, log, max, min, pi, pow, random, sin, sqrt) are available, but
    -- nothing else: sandboxing!
    setfenv(chunk, math)
    local rc, result = pcall(chunk)
    if not rc then return nil, result end

    result = tostring(result)
    return string.gsub(result, ",", ".")
end


--
-- A button has been pressed
--
function CALC:btn_click(c, op)
    local s2
    local s, pos, msg = c.entry:get_text(), -100, ""

    if op == '=' then
	if s ~= "" then
	    s2, msg = calculate(s)
	    if s2 then
		s = s2
		msg = ""
	    end
	end
	pos = -1
    elseif op == 'AC' then
	s = ""
    elseif op == 'EXP' then
	s = s .. "^"
    elseif op == 'Ans' then
	s = s .. "$ans"		-- not working
    elseif op == 'DEL' then
	s = s:sub(1, s:len() - 1)
    else
	s = s .. op
    end

    c.entry:set_text(s)
    c.message:set_text(msg)
    if pos ~= -100 then c.entry:set_position(pos) end
end


function my_insert_text(entry, txt, len, posptr, userdata)
    print("insert text", entry, txt, len, posptr, userdata)
end

--
-- Create a CALC object with a stack and the window.  Add buttons.
--
function CALC.new()

    local tbl
    local vbox, hbox, btn

    local c = { stack={} }
    setmetatable(c, CALC)

    c.win = gtk.window_new(gtk.WINDOW_TOPLEVEL)
    c.win:connect('delete-event', gtk.main_quit)
    c.win:set_title('Calculator')

    tbl = gtk.table_new(6, 5, true)	-- rows, cols, homogenous
    c.win:add(tbl)

    -- entry field
    c.entry = gtk.entry_new()
    c.entry:set_alignment(0.9)
    c.entry:set_activates_default(true)
    -- c.entry:connect('insert-text', my_insert_text)
    tbl:attach_defaults(c.entry, 0, 5, 0, 1)

    -- error message field
    c.message = gtk.entry_new()
    c.message:set_editable(false)
    tbl:attach_defaults(c.message, 0, 5, 1, 2)

    -- buttons
    local buttons = { '7', '8', '9', 'DEL', 'AC',
	'4', '5', '6', '*', '/',
	'1', '2', '3', '+', '-',
	0, '.', 'EXP', 'Ans', '=' }

    local row = 1
    local col = 0
    for nr, lbl in pairs(buttons) do
	if math.mod(nr, 5) == 1 then
	    row = row + 1
	    col = 0
	end

	btn = gtk.button_new_with_label(lbl)
	tbl:attach_defaults(btn, col, col + 1, row, row + 1)
	btn:connect('clicked', CALC.btn_click, c, lbl)
	if lbl == '=' then
	    btn.flags = btn.flags + gtk.CAN_DEFAULT
	    btn:grab_default()
	end
	col = col + 1
    end

    c.win:show_all()
    return c
end

local calc = CALC.new()
gtk.main()