1---------------------
2-- "KhiCAS" : TI-Nspire Giac UI in Nspire-Lua
3-- Version 1.05 - 18/06/2014
4-- GPL v3 License
5-- http://tiplanet.org/forum/viewtopic.php?f=43&t=14800
6---------------------
7-- Giac CAS engine by Bernard Parisse
8-- http://www-fourier.ujf-grenoble.fr/~parisse/
9---------------------
10-- The UI is highly based on Xavier 'critor' Andréani's "SuperSpire"
11-- See http://tiplanet.org/forum/viewtopic.php?t=13851&p=157015
12-------------------
13-- ETK GUI Lib by Jim Bauwens and Adrien "Adriweb" Bertrand
14-- Additions/mods by Xavier
15-- some more little changes by Adrien
16-- (borders, horiz. lines, readonly, copy/paste, errHandler etc.)
17-------------------
18
19platform.apilevel = '2.0'
20
21hasGiac = pcall(nrequire, "luagiac")
22if not hasGiac then
23	luagiac = { caseval = function(str) return math.evalStr(str) end }
24	print("Giac module not loaded ! Fallback on Nspire's math engine.")
25end
26
27-- localized useful functions
28
29local mathmin = math.min
30local mathmax = math.max
31local mathrandom = math.random
32
33ts = 0.0
34
35
36-- ETK Stuff
37
38--------------------------------------------------------------------- View: Widgets & Events manager
39
40defaultFocus = nil
41
42View = class()
43
44function View:init(window)
45	self.window = window
46	self.widgetList = {}
47	self.focusList = {}
48	self.currentFocus = 0
49	self.currentCursor = "default"
50
51	-- Previous location of mouse pointer
52	self.prev_mousex = 0
53	self.prev_mousey = 0
54end
55
56
57function View:invalidate()
58	self.window:invalidate()
59end
60
61function View:setCursor(cursor)
62	if cursor ~= self.currentCursor then
63		self.currentCursor = cursor
64		self:invalidate()
65	end
66end
67
68
69function View:add(o)
70	table.insert(self.widgetList, o)
71	self:repos(o)
72	if o.acceptsFocus then
73		table.insert(self.focusList, 1, o)
74		if self.currentFocus > 0 then
75			self.currentFocus = self.currentFocus + 1
76		end
77	end
78	return o
79end
80
81function View:remove(o)
82	if self:getFocus() == o then
83		o:releaseFocus()
84	end
85	local i = 1
86	local f = 0
87	local oldf
88	while i <= #self.focusList do
89		if self.focusList[i] == o then
90			f = i
91		end
92		i = i + 1
93	end
94	if f > 0 then
95		if self:getFocus() == o then
96			self:tabForward()
97		end
98		table.remove(self.focusList, f)
99		if self.currentFocus > f then
100			self.currentFocus = self.currentFocus - 1
101		end
102	end
103	f = 0
104	i = 1
105	while i <= #self.widgetList do
106		if self.widgetList[i] == o then
107			f = i
108		end
109		i = i + 1
110	end
111	if f > 0 then
112		table.remove(self.widgetList, f)
113	end
114	--    table.remove(self.widgetList, o)
115	--    table.remove(self.focusList, o)
116end
117
118function View:repos(o)
119	local x = o.x
120	local y = o.y
121	local w = o.w
122	local h = o.h
123	if o.hConstraint == "right" then
124		x = scrWidth - o.w - o.dx1
125	elseif o.hConstraint == "center" then
126		x = (scrWidth - o.w + o.dx1) / 2
127	elseif o.hConstraint == "justify" then
128		w = scrWidth - o.x - o.dx1
129	end
130	if o.vConstraint == "bottom" then
131		y = scrHeight - o.h - o.dy1
132	elseif o.vConstraint == "middle" then
133		y = (scrHeight - o.h + o.dy1) / 2
134	elseif o.vConstraint == "justify" then
135		h = scrHeight - o.y - o.dy1
136	end
137	o:repos(x, y)
138	o:resize(w, h)
139end
140
141function View:resize()
142	for _, o in ipairs(self.widgetList) do
143		self:repos(o)
144	end
145end
146
147function View:hide(o)
148	if o.visible then
149		o.visible = false
150		self:releaseFocus(o)
151		if o:contains(self.prev_mousex, self.prev_mousey) then
152			o:onMouseLeave(o.x - 1, o.y - 1)
153		end
154		self:invalidate()
155	end
156end
157
158function View:show(o)
159	if not o.visible then
160		o.visible = true
161		if o:contains(self.prev_mousex, self.prev_mousey) then
162			o:onMouseEnter(self.prev_mousex, self.prev_mousey)
163		end
164		self:invalidate()
165	end
166end
167
168function View:getFocus()
169	if self.currentFocus == 0 then
170		return nil
171	end
172	return self.focusList[self.currentFocus]
173end
174
175function View:setFocus(obj)
176	if self.currentFocus ~= 0 then
177		if self.focusList[self.currentFocus] == obj then
178			return
179		end
180		self.focusList[self.currentFocus]:releaseFocus()
181	end
182	self.currentFocus = 0
183	for i = 1, #self.focusList do
184		if self.focusList[i] == obj then
185			self.currentFocus = i
186			obj:setFocus()
187			self:invalidate()
188			break
189		end
190	end
191end
192
193function View:releaseFocus(obj)
194	if self.currentFocus ~= 0 then
195		if self.focusList[self.currentFocus] == obj then
196			self.currentFocus = 0
197			obj:releaseFocus()
198			self:invalidate()
199		end
200	end
201end
202
203function View:sendStringToFocus(str)
204	local o = self:getFocus()
205	if not o then
206		o = defaultFocus
207		self:setFocus(o)
208	end
209	if o then
210		if o.visible then
211			if o:addString(str) then
212				self:invalidate()
213			else
214				o = nil
215			end
216		end
217	end
218
219	if not o then -- look for a default handler
220		for _, o in ipairs(self.focusList) do
221			if o.visible then
222				if o:addString(str) then
223					self:setFocus(o)
224					self:invalidate()
225					break
226				end
227			end
228		end
229	end
230end
231
232
233function View:backSpaceHandler()
234	-- Does the focused widget accept BackSpace?
235	local o = self:getFocus()
236	if o then
237		if o.visible and o.acceptsBackSpace then
238			o:backSpaceHandler()
239			self:setFocus(o)
240			self:invalidate()
241		else
242			o = nil
243		end
244	end
245	if not o then -- look for a default handler
246		for _, o in ipairs(self.focusList) do
247			if o.visible and o.acceptsBackSpace then
248				o:backSpaceHandler()
249				self:setFocus(o)
250				self:invalidate()
251				break;
252			end
253		end
254	end
255end
256
257
258function View:tabForward()
259	local nextFocus = self.currentFocus + 1
260	if nextFocus > #self.focusList then
261		nextFocus = 1
262	end
263	self:setFocus(self.focusList[nextFocus])
264	if self:getFocus() then
265		if not self:getFocus().visible then
266			self:tabForward()
267		end
268	end
269	self:invalidate()
270end
271
272
273function View:tabBackward()
274	local nextFocus = self.currentFocus - 1
275	if nextFocus < 1 then
276		nextFocus = #self.focusList
277	end
278	self:setFocus(self.focusList[nextFocus])
279	if not self:getFocus().visible then
280		self:tabBackward()
281	end
282	self:invalidate()
283end
284
285
286function View:onMouseDown(x, y)
287	-- Find a widget that has a mouse down handler and bounds the click point
288	for _, o in ipairs(self.widgetList) do
289		if o.visible and o.acceptsFocus and o:contains(x, y) then
290			self.mouseCaptured = o
291			o:onMouseDown(o, window, x - o.x, y - o.y)
292			self:setFocus(o)
293			self:invalidate()
294			return
295		end
296	end
297	if self:getFocus() then
298		self:setFocus(nil)
299		self:invalidate()
300	end
301end
302
303
304function View:onMouseMove(x, y)
305	local prev_mousex = self.prev_mousex
306	local prev_mousey = self.prev_mousey
307	for _, o in ipairs(self.widgetList) do
308		local xyin = o:contains(x, y)
309		local prev_xyin = o:contains(prev_mousex, prev_mousey)
310		if xyin and not prev_xyin and o.visible then
311			-- Mouse entered widget
312			o:onMouseEnter(x, y)
313			self:invalidate()
314		elseif prev_xyin and (not xyin or not o.visible) then
315			-- Mouse left widget
316			o:onMouseLeave(x, y)
317			self:invalidate()
318		end
319	end
320	self.prev_mousex = x
321	self.prev_mousey = y
322end
323
324
325function View:onMouseUp(x, y)
326	local mc = self.mouseCaptured
327	if mc then
328		self.mouseCaptured = nil
329		if mc:contains(x, y) then
330			mc:onMouseUp(x - mc.x, y - mc.y)
331		else
332			--            mc:cancelClick()
333		end
334	end
335end
336
337
338function View:enterHandler()
339	-- Does the focused widget accept Enter?
340	local o = self:getFocus()
341	if o then
342		if o.visible and o.acceptsEnter then
343			o:enterHandler()
344			self:setFocus(o)
345			self:invalidate()
346		else
347			o = nil
348		end
349	end
350	if not o then -- look for a default handler
351		for _, o in ipairs(self.focusList) do
352			if o.visible and o.acceptsEnter then
353				o:enterHandler()
354				self:setFocus(o)
355				self:invalidate()
356				break;
357			end
358		end
359	end
360end
361
362function View:arrowLeftHandler()
363	-- Does the focused widget accept ArrowLeft?
364	local o = self:getFocus()
365	if o then
366		if o.visible and o.acceptsArrowLeft then
367			o:arrowLeftHandler()
368			self:setFocus(o)
369			self:invalidate()
370		else
371			o = nil
372		end
373	end
374	if not o then -- look for a default handler
375		for _, o in ipairs(self.focusList) do
376			if o.visible and o.acceptsArrowLeft then
377				o:arrowLeftHandler()
378				self:setFocus(o)
379				self:invalidate()
380				break;
381			end
382		end
383	end
384end
385
386function View:arrowRightHandler()
387	-- Does the focused widget accept ArrowRight?
388	local o = self:getFocus()
389	if o then
390		if o.visible and o.acceptsArrowRight then
391			o:arrowRightHandler()
392			self:setFocus(o)
393			self:invalidate()
394		else
395			o = nil
396		end
397	end
398	if not o then -- look for a default handler
399		for _, o in ipairs(self.focusList) do
400			if o.visible and o.acceptsArrowRight then
401				o:arrowRightHandler()
402				self:setFocus(o)
403				self:invalidate()
404				break;
405			end
406		end
407	end
408end
409
410function View:arrowUpHandler()
411	-- Does the focused widget accept ArrowUp?
412	local o = self:getFocus()
413	if o then
414		if o.visible and o.acceptsArrowUp then
415			o:arrowUpHandler()
416			self:setFocus(o)
417			self:invalidate()
418		else
419			o = nil
420		end
421	end
422	if not o then -- look for a default handler
423		for _, o in ipairs(self.focusList) do
424			if o.visible and o.acceptsArrowUp then
425				o:arrowUpHandler()
426				self:setFocus(o)
427				self:invalidate()
428				break;
429			end
430		end
431	end
432end
433
434function View:arrowDownHandler()
435	-- Does the focused widget accept ArrowDown?
436	local o = self:getFocus()
437	if o then
438		if o.visible and o.acceptsArrowDown then
439			o:arrowDownHandler()
440			self:setFocus(o)
441			self:invalidate()
442		else
443			o = nil
444		end
445	end
446	if not o then -- look for a default handler
447		for _, o in ipairs(self.focusList) do
448			if o.visible and o.acceptsArrowDown then
449				o:arrowDownHandler()
450				self:setFocus(o)
451				self:invalidate()
452				break;
453			end
454		end
455	end
456end
457
458function View:paint(gc)
459	local fo = self:getFocus()
460	for _, o in ipairs(self.widgetList) do
461		if o.visible then
462			o:paint(gc, fo == o)
463			if fo == o then
464				gc:setColorRGB(100, 150, 255)
465				gc:drawRect(o.x - 1, o.y - 1, o.w + 1, o.h + 1)
466				gc:setPen("thin", "smooth")
467				gc:setColorRGB(0)
468			end
469		end
470	end
471	cursor.set(self.currentCursor)
472end
473
474theView = nil
475
476--------------------------------------------------------------------- Widget
477
478Widget = class()
479
480function Widget:setHConstraints(hConstraint, dx1)
481	self.hConstraint = hConstraint
482	self.dx1 = dx1
483end
484
485function Widget:setVConstraints(vConstraint, dy1)
486	self.vConstraint = vConstraint
487	self.dy1 = dy1
488end
489
490function Widget:init(view, x, y, w, h)
491	self.xOrig = x
492	self.yOrig = y
493	self.view = view
494	self.x = x
495	self.y = y
496	self.w = w
497	self.h = h
498	self.acceptsFocus = false
499	self.visible = true
500	self.acceptsEnter = false
501	self.acceptsEscape = false
502	self.acceptsTab = false
503	self.acceptsDelete = false
504	self.acceptsBackSpace = false
505	self.acceptsReturn = false
506	self.acceptsArrowUp = false
507	self.acceptsArrowDown = false
508	self.acceptsArrowLeft = false
509	self.acceptsArrowRight = false
510	self.hConstraint = "left"
511	self.vConstraint = "top"
512end
513
514function Widget:repos(x, y)
515	self.x = x
516	self.y = y
517end
518
519function Widget:resize(w, h)
520	self.w = w
521	self.h = h
522end
523
524function Widget:setFocus()
525end
526
527function Widget:releaseFocus()
528end
529
530function Widget:contains(x, y)
531	return x >= self.x and x <= self.x + self.w
532			and y >= self.y and y <= self.y + self.h
533end
534
535
536function Widget:onMouseEnter(x, y)
537	-- Implemented in subclasses
538end
539
540
541function Widget:onMouseLeave(x, y)
542	-- Implemented in subclasses
543end
544
545function Widget:paint(gc, focused)
546	-- Implemented in subclasses
547end
548
549function Widget:enterHandler()
550end
551
552function Widget:escapeHandler()
553end
554
555function Widget:tabHandler()
556end
557
558function Widget:deleteHandler()
559end
560
561function Widget:backSpaceHandler()
562end
563
564function Widget:returnHandler()
565end
566
567function Widget:arrowUpHandler()
568end
569
570function Widget:arrowDownHandler()
571end
572
573function Widget:arrowLeftHandler()
574end
575
576function Widget:arrowRightHandler()
577end
578
579function Widget:onMouseDown(x, y)
580end
581
582function Widget:onMouseUp(x, y)
583end
584
585--------------------------------------------------------------------- Button widget
586
587Button = class(Widget)
588
589function Button:init(view, x, y, w, h, default, command, shortcut)
590	Widget.init(self, view, x, y, w, h)
591	-- Button configuration
592	self.acceptsFocus = true
593	self.acceptsBackspace = false
594	self.command = command or function() end -- what to do when pressed
595	self.default = default -- is default button when ENTER is pressed
596	self.shortcut = shortcut
597	-- Current button state
598	self.clicked = false
599	self.highlighted = false
600	self.acceptsEnter = true
601end
602
603-- Act on key press on button
604function Button:enterHandler()
605	if self.acceptsEnter then
606		self:command()
607	end
608end
609
610function Button:escapeHandler()
611	if self.acceptsEscape then
612		self:command()
613	end
614end
615
616function Button:tabHandler()
617	if self.acceptsTab then
618		self:command()
619	end
620end
621
622function Button:deleteHandler()
623	if self.acceptsDelete then
624		self:command()
625	end
626end
627
628function Button:backSpaceHandler()
629	if self.acceptsBackSpace then
630		self:command()
631	end
632end
633
634function Button:returnHandler()
635	if self.acceptsReturn then
636		self:command()
637	end
638end
639
640function Button:arrowUpHandler()
641	if self.acceptsArrowUp then
642		self:command()
643	end
644end
645
646function Button:arrowDownHandler()
647	if self.acceptsArrowDown then
648		self:command()
649	end
650end
651
652function Button:arrowLeftHandler()
653	if self.acceptsArrowLeft then
654		self:command()
655	end
656end
657
658function Button:arrowRightHandler()
659	if self.acceptsArrowRight then
660		self:command()
661	end
662end
663
664function Button:arrowUpHandler()
665	if self.acceptsArrowUp then
666		self:command()
667	end
668end
669
670function Button:arrowDownHandler()
671	if self.acceptsArrowDown then
672		self:command()
673	end
674end
675
676function Button:onMouseDown(x, y)
677	self.clicked = true
678	self.highlighted = true
679end
680
681function Button:onMouseEnter(x, y)
682	theView:setCursor("hand pointer")
683	if self.clicked and not self.highlighted then
684		self.highlighted = true
685	end
686end
687
688function Button:onMouseLeave(x, y)
689	theView:setCursor("default")
690	if self.clicked and self.highlighted then
691		self.highlighted = false
692	end
693end
694
695function Button:cancelClick()
696	if self.clicked then
697		self.highlighted = false
698		self.clicked = false
699	end
700end
701
702function Button:onMouseUp(x, y)
703	self:cancelClick()
704	self:command()
705end
706
707function Button:addString(str)
708	if str == " " or str == self.shortcut then
709		self:command()
710		return true
711	end
712	return false
713end
714
715--------------------------------------------------------------------- ImgLabel widget
716
717ImgLabel = class(Widget)
718
719function ImgLabel:init(view, x, y, img)
720	self.img = image.new(img)
721	self.w = image.width(self.img)
722	self.h = image.height(self.img)
723	Widget.init(self, view, x, y, self.w, self.h, false, command, shortcut)
724end
725
726function ImgLabel:paint(gc, focused)
727	gc:drawImage(self.img, self.x, self.y)
728end
729
730--------------------------------------------------------------------- ImgButton widget
731
732ImgButton = class(Button)
733
734function ImgButton:init(view, x, y, img, command, shortcut)
735	self.img = image.new(img)
736	self.w = image.width(self.img)
737	self.h = image.height(self.img)
738	Button.init(self, view, x, y, self.w, self.h, false, command, shortcut)
739end
740
741function ImgButton:paint(gc, focused)
742	gc:drawImage(self.img, self.x, self.y)
743end
744
745--------------------------------------------------------------------- TextButton widget
746
747TextButton = class(Button)
748
749function TextButton:init(view, x, y, text, command, shortcut)
750	self.textid = text
751	self.text = getLocaleText(text)
752	self:resize(0, 0)
753	Button.init(self, view, x, y, self.w, self.h, false, command, shortcut)
754end
755
756function TextButton:resize(w, h)
757	self.text = getLocaleText(self.textid)
758	self.w = getStringWidth(self.text) + 5
759	self.h = getStringHeight(self.text) + 5
760end
761
762
763function TextButton:paint(gc, focused)
764	gc:setColorRGB(223, 223, 223)
765	gc:drawRect(self.x + 1, self.y + 1, self.w - 2, self.h - 2)
766	gc:setColorRGB(191, 191, 191)
767	gc:fillRect(self.x + 1, self.y + 1, self.w - 3, self.h - 3)
768	gc:setColorRGB(223, 223, 223)
769	gc:drawString(self.text, self.x + 3, self.y + 3, "top")
770	gc:setColorRGB(0)
771	gc:drawString(self.text, self.x + 2, self.y + 2, "top")
772	gc:drawRect(self.x, self.y, self.w - 2, self.h - 2)
773end
774
775--------------------------------------------------------------------- vertical scroll bar
776VScrollBar = class(Widget)
777
778function VScrollBar:init(view, x, y, w, h)
779	self.pos = 10
780	self.siz = 10
781	Widget.init(self, view, x, y, w, h, false)
782end
783
784function VScrollBar:paint(gc, focused)
785	gc:setColorRGB(0)
786	gc:drawRect(self.x, self.y, self.w, self.h)
787	gc:fillRect(self.x + 2, self.y + self.h - (self.h - 4) * (self.pos + self.siz) / 100 - 2, self.w - 3, mathmax(1, (self.h - 4) * self.siz / 100 + 1))
788end
789
790--------------------------------------------------------------------- Text widget
791
792TextLabel = class(Widget)
793
794function TextLabel:init(view, x, y, text)
795	self:setText(text)
796	Widget.init(self, view, x, y, self.w, self.h, false)
797end
798
799function TextLabel:resize(w, h)
800	self.text = getLocaleText(self.textid)
801	self.w = getStringWidth(self.text)
802	self.h = getStringHeight(self.text)
803end
804
805function TextLabel:setText(text)
806	self.textid = text
807	self.text = getLocaleText(text)
808	self:resize(0, 0)
809end
810
811function TextLabel:getText()
812	return self.text
813end
814
815function TextLabel:paint(gc, focused)
816	gc:setColorRGB(0)
817	gc:drawString(self.text, self.x, self.y, "top")
818end
819
820--------------------------------------------------------------------- editable RichText widget
821
822RichTextEditor = class(Widget)
823
824function RichTextEditor:init(view, x, y, w, h, text)
825	self.editor = D2Editor.newRichText()
826	self.readOnly = false
827	self:repos(x, y)
828	self.editor:setFontSize(fsize)
829	self.editor:setFocus(false)
830	self.text = text
831	self:resize(w, h)
832	Widget.init(self, view, x, y, self.w, self.h, true)
833	self.acceptsFocus = true
834	self.editor:setExpression(text)
835	self.editor:setBorder(1)
836end
837
838function RichTextEditor:onMouseEnter(x, y)
839	theView:setCursor("text")
840end
841
842function RichTextEditor:onMouseLeave(x, y)
843	theView:setCursor("default")
844end
845
846function RichTextEditor:repos(x, y)
847	if not self.editor then self = nil; return; end
848	self.editor:setBorderColor((showEditorsBorders and 0) or 0xffffff)
849	self.editor:move(x+1, y+1)
850	Widget.repos(self, x, y)
851end
852
853function RichTextEditor:resize(w, h)
854	if not self.editor then self = nil; return; end
855	self.editor:resize(w-1, h-1)
856	Widget.resize(self, w, h)
857end
858
859function RichTextEditor:setFocus()
860	self.editor:setFocus(true)
861end
862
863function RichTextEditor:releaseFocus()
864	self.editor:setFocus(false)
865end
866
867function RichTextEditor:addString(str)
868	local currentText = self.editor:getText() or ""
869	self.editor:setText(currentText .. str)
870	return true
871end
872
873
874function RichTextEditor:paint(gc, focused)
875	--    self.editor:paint(gc)
876end
877
878--------------------------------------------------------------------- editable Math widget
879
880MathEditor = class(RichTextEditor)
881
882-- pretty printed square root characters from MathBoxes take two 'special' characters in returned expression string
883-- cursor position in expression returned by getExpression() then does not match cursor position in string after the square root
884function string.ulen(s)
885	if not s then return 0 else return select(2, s:gsub("[^\128-\193]", "")) end
886end
887ulen = string.ulen
888
889function MathEditor:init(view, x, y, w, h, text)
890	RichTextEditor.init(self, view, x, y, w, h, text)
891	self.editor:setBorder(1)
892	self.acceptsEnter = true
893	self.acceptsBackSpace = true
894	self.result = false
895	-- add editor focus listener which does: setFocus(getME(editor))
896	self.editor:registerFilter({
897		arrowLeft = function()
898			local _, curpos = self.editor:getExpressionSelection()
899			if curpos < 7 then
900				on.arrowLeft()
901				return true
902			end
903			return false
904		end,
905		arrowRight = function()
906			local currentText, curpos = self.editor:getExpressionSelection()
907			if curpos > ulen(currentText) - 2 then
908				on.arrowRight()
909				return true
910			end
911			return false
912		end,
913		tabKey = function()
914			theView:tabForward()
915			return true
916		end,
917		mouseDown = function(x, y)
918			theView:onMouseDown(x, y)
919			return false
920		end,
921		backspaceKey = function()
922			if (self == fctEditor) then
923				self:fixCursor()
924				local _, curpos = self.editor:getExpressionSelection()
925				if curpos <= 6 then return true end
926				return false
927			else
928				self:backSpaceHandler()
929				return true
930			end
931		end,
932		deleteKey = function()
933			if (self == fctEditor) then
934				self:fixCursor()
935				local currentText, curpos = self.editor:getExpressionSelection()
936				if curpos >= ulen(currentText) - 1 then return true end
937				return false
938			else
939				self:backSpaceHandler()
940				return true
941			end
942		end,
943		enterKey = function()
944			self:enterHandler()
945			return true
946		end,
947		returnKey = function()
948			theView:enterHandler()
949			return true
950		end,
951		escapeKey = function()
952			on.escapeKey()
953			return true
954		end,
955		clearKey = function()
956			if self == fctEditor then
957				self.editor:setExpression("")
958				self:fixContent()
959			else
960				self:backSpaceHandler()
961			end
962			return true
963		end,
964		charIn = function(c)
965			if (self == fctEditor) then
966				if self.editor:getExpression() then
967					self:fixCursor()
968				end
969				return false
970			else
971				return self.readOnly
972			end
973		end
974	})
975end
976
977function MathEditor:fixContent()
978	local currentText = self.editor:getExpressionSelection()
979	if not currentText or currentText == "" then
980		self.editor:createMathBox()
981	end
982end
983
984function MathEditor:fixCursor()
985	local currentText, curpos, selstart = self.editor:getExpressionSelection()
986	local l = ulen(currentText)
987	if curpos < 6 or selstart < 6 or curpos > l - 1 or selstart > l - 1 then
988		if curpos < 6 then curpos = 6 end
989		if selstart < 6 then selstart = 6 end
990		if curpos > l - 1 then curpos = l - 1 end
991		if selstart > l - 1 then selstart = l - 1 end
992		self.editor:setExpression(currentText, curpos, selstart)
993	end
994end
995
996function MathEditor:getExpression()
997	if not self.editor then self = nil; return ""; end
998	local rawexpr = self.editor:getExpression()
999	local expr = ""
1000	local n = rawexpr:len()
1001	local b = 0
1002	local bs = 0
1003	local bi = 0
1004	local status = 0
1005	local i = 1
1006	local c
1007	while i <= n do
1008		c = rawexpr:sub(i, i)
1009		if c == "{" then
1010			b = b + 1
1011		elseif c == "}" then
1012			b = b - 1
1013		end
1014		if status == 0 then
1015			if rawexpr:sub(i, i + 5) == "\\0el {" then
1016				bs = i + 6
1017				i = i + 5
1018				status = 1
1019				bi = b
1020				b = b + 1
1021			end
1022		else
1023			if b == bi then
1024				status = 0
1025				expr = expr .. rawexpr:sub(bs, i - 1)
1026			end
1027		end
1028		i = i + 1
1029	end
1030	return expr
1031end
1032
1033function MathEditor:setFocus()
1034	if not self.editor then self = nil; return; end
1035	self.editor:setFocus(true)
1036end
1037
1038function MathEditor:releaseFocus()
1039	if not self.editor then self = nil; return; end
1040	self.editor:setFocus(false)
1041end
1042
1043function MathEditor:addString(str)
1044	if not self.editor then self = nil; return; end
1045	self:fixCursor()
1046	local currentText, curpos, selstart = self.editor:getExpressionSelection()
1047	currentText = currentText:usub(1, mathmin(curpos, selstart)) .. str .. currentText:usub(mathmax(curpos, selstart) + 1, ulen(currentText))
1048	self.editor:setExpression(currentText, mathmin(curpos, selstart) + ulen(str))
1049	return true
1050end
1051
1052function MathEditor:backSpaceHandler()
1053	backSpaceHandler(self)
1054end
1055
1056function MathEditor:enterHandler()
1057	enterHandler(self)
1058end
1059
1060function MathEditor:paint(gc)
1061	if not self.editor then self = nil; return; end
1062	if showHLines and not self.result then
1063		gc:setColorRGB(100, 100, 100)
1064		local ycoord = self.y - (showEditorsBorders and 0 or 2)
1065		gc:drawLine(1, ycoord, platform.window:width() - sbv.w - 2, ycoord)
1066		gc:setColorRGB(0)
1067	end
1068end
1069
1070--------------------------------------------------------------------- events handling
1071function on.arrowUp()
1072	if theView:getFocus() == fctEditor then
1073		on.tabKey()
1074	else
1075		on.tabKey()
1076		if theView:getFocus() ~= fctEditor then on.tabKey() end
1077	end
1078	reposView()
1079end
1080
1081function on.arrowDown()
1082	if theView:getFocus() == fctEditor then return end
1083	on.backtabKey()
1084	if theView:getFocus() ~= fctEditor then on.backtabKey() end
1085	reposView()
1086end
1087
1088function on.arrowLeft()
1089	if theView:getFocus() == fctEditor then return end
1090	on.tabKey()
1091	reposView()
1092end
1093
1094function on.arrowRight()
1095	if theView:getFocus() == fctEditor then return end
1096	on.backtabKey()
1097	reposView()
1098end
1099
1100function on.charIn(ch)
1101	theView:sendStringToFocus(ch)
1102end
1103
1104function on.tabKey()
1105	theView:tabForward()
1106	reposView()
1107end
1108
1109function on.backtabKey()
1110	theView:tabBackward()
1111	reposView()
1112end
1113
1114function on.escapeKey()
1115	-- nothing to do ?
1116end
1117
1118function on.enterKey()
1119	theView:enterHandler()
1120end
1121on.returnKey = on.enterKey
1122
1123function on.mouseMove(x, y)
1124	theView:onMouseMove(x, y)
1125end
1126
1127function on.mouseDown(x, y)
1128	theView:onMouseDown(x, y)
1129	--		theView:invalidate()
1130end
1131
1132function on.mouseUp(x, y)
1133	theView:onMouseUp(x, y)
1134end
1135
1136function initFontGC(gc)
1137	gc:setFont(font, style, fsize)
1138end
1139
1140function getStringHeightGC(text, gc)
1141	initFontGC(gc)
1142	return gc:getStringHeight(text)
1143end
1144
1145function getStringHeight(text)
1146	return platform.withGC(getStringHeightGC, text)
1147end
1148
1149function getStringWidthGC(text, gc)
1150	initFontGC(gc)
1151	return gc:getStringWidth(text)
1152end
1153
1154function getStringWidth(text)
1155	return platform.withGC(getStringWidthGC, text)
1156end
1157
1158function initGUI()
1159	showEditorsBorders = false
1160	showHLines = true
1161	scrWidth = platform.window:width()
1162	scrHeight = platform.window:height()
1163	if (scrWidth > 0 or scrHeight > 0) then
1164		theView = View(platform.window)
1165		sbv = VScrollBar(theView, 0, -1, 5, scrHeight + 1)
1166		sbv:setHConstraints("right", 0)
1167		theView:add(sbv)
1168		fctEditor = MathEditor(theView, 2, border, 50, 30, "")
1169		-- fctEditor = MathEditor(theView, border, border, 50, 30, "")
1170		fctEditor:setHConstraints("justify", 1)
1171		-- fctEditor:setHConstraints("justify", border + scrWidth - sbv.x)
1172		fctEditor:setVConstraints("bottom", 1)
1173		fctEditor.editor:setSizeChangeListener(function(editor, w, h)
1174			return resizeME(editor, w, h)
1175		end)
1176		theView:add(fctEditor)
1177		fctEditor.editor:setText("")
1178		fctEditor.editor:setBorder(0)
1179		fctEditor:fixContent()
1180		sbv:setVConstraints("justify", scrHeight - fctEditor.y + border)
1181		theView:setFocus(fctEditor)
1182		inited = true
1183	end
1184	toolpalette.enableCopy(true)
1185	toolpalette.enablePaste(true)
1186end
1187
1188function resizeGC(gc)
1189	scrWidth = platform.window:width()
1190	scrHeight = platform.window:height()
1191	if not inited then
1192		initGUI()
1193	end
1194	if inited then
1195		initFontGC(gc)
1196		strFullHeight = gc:getStringHeight("H")
1197		strHeight = strFullHeight - 3
1198		theView:resize()
1199		reposME()
1200		theView:invalidate()
1201	end
1202end
1203
1204function on.resize()
1205	platform.withGC(resizeGC)
1206end
1207
1208forcefocus = true
1209function on.activate()
1210	forcefocus = true
1211end
1212
1213dispinfos = true
1214function on.paint(gc)
1215	if not inited then
1216		initGUI()
1217		initFontGC(gc)
1218		strFullHeight = gc:getStringHeight("H")
1219		strHeight = strFullHeight - 3
1220	end
1221	if inited then
1222		local obj
1223		obj = theView:getFocus()
1224		initFontGC(gc)
1225		if not obj then theView:setFocus(fctEditor) end
1226		if (forcefocus) then
1227			if obj == fctEditor then
1228				fctEditor.editor:setFocus(true)
1229				if fctEditor.editor:hasFocus() then forcefocus = false end
1230			else
1231				forcefocus = false
1232			end
1233		end
1234		if dispinfos then
1235			gc:setColorRGB(0)
1236			gc:setFont("sansserif", "r", 10)
1237			gc:drawString("Giac CAS engine :", 2, 0, "top")
1238			if hasGiac then
1239				gc:setColorRGB(0, 127, 0)
1240				gc:drawString("OK.", gc:getStringWidth("Giac CAS engine :") + 6, 0, "top")
1241				gc:setColorRGB(0)
1242				gc:drawString("Giac (c) B. Parisse/R. De Graeve, license GPL3", 2, 1 * strHeight, "top")
1243				gc:drawString("Not allowed during exams if", 2, 2 * strHeight, "top")
1244				gc:drawString("CAS calculators are forbidden!", 2, 3 * strHeight, "top")
1245				gc:drawString("Type ?commandname for short help", 2, scrHeight/2-20, "top")
1246				gc:drawString("Beta version, report bugs/issues you encounter !", 2, scrHeight/2, "top")
1247			else
1248				gc:setColorRGB(255, 0, 0)
1249				gc:drawString("NO.", gc:getStringWidth("Giac CAS engine :") + 6, 0, "top")
1250				gc:setColorRGB(0)
1251				gc:drawString("Make sure to have the .luax file and Ndless installed!", 2, strHeight, "top")
1252				gc:drawString("Hint: run ndless_installer again", 2, 2 * strHeight, "top")
1253				gc:setFont("sansserif", "i", 10)
1254				gc:drawString("Fallback on the Nspire's math engine.", 2, 3 * strHeight + 8, "top")
1255			end
1256			gc:setFont("sansserif", "r", fsize)
1257		end
1258		theView:paint(gc)
1259		-- gc:drawString(ts, 2, 0, "top")
1260		gc:drawRect(0, fctEditor.y - 2, scrWidth, fctEditor.y - 2)
1261	end
1262end
1263
1264--------------------------------------------------------------------- global variables
1265
1266font = "sansserif"
1267style = "r"
1268fsize = 12
1269
1270scrWidth = 0
1271scrHeight = 0
1272inited = false
1273delim = " ≟ "
1274border = 3
1275
1276strHeight = 0
1277strFullHeight = 0
1278
1279--------------------------------------------------------------------- global functions
1280
1281evalstr = false
1282
1283histME1 = {}
1284histME2 = {}
1285
1286function addME(expr, res)
1287	local mee = MathEditor(theView, border, border, 50, 30, "")
1288	mee.readOnly = true
1289	table.insert(histME1, mee)
1290	mee:setHConstraints("left", border)
1291	mee.editor:setSizeChangeListener(function(editor, w, h)
1292		return resizeME(editor, w + 3, h)
1293	end)
1294	mee.editor:setExpression("\\0el {" .. expr .. "}", 0)
1295	mee:fixCursor()
1296	mee.editor:setReadOnly(true)
1297	theView:add(mee)
1298
1299	local mer = MathEditor(theView, border, border, 50, 30, "")
1300	mer.result = true
1301	mer.readOnly = true
1302	table.insert(histME2, mer)
1303	mer:setHConstraints("right", scrWidth - sbv.x + border)
1304	mer.editor:setSizeChangeListener(function(editor, w, h)
1305		return resizeMEpar(editor, w + border, h)
1306	end)
1307	mer.editor:setExpression("\\0el {" .. res .. "}", 0)
1308	mer:fixCursor()
1309	mer.editor:setReadOnly(true)
1310	theView:add(mer)
1311	reposME()
1312end
1313
1314limpsuff = "(+)"
1315limmsuff = "(-)"
1316function cleanAns1(expr)
1317	local a, b = expr:find(delim, 1, true)
1318	local c = 0
1319	local i = 1
1320	local l = string.len(expr)
1321	if a then
1322		c = expr:find(".", 1, true)
1323		if not c then c = 0 end
1324		if c == 0 or c > b then
1325			expr = expr:sub(1, a - 1)
1326		else
1327			expr = expr:sub(b + 1, l)
1328		end
1329		return cleanAns1(expr)
1330	end
1331	l = string.len(expr)
1332	a, b = expr:find(limpsuff, 1, true)
1333	if (not (a)) then
1334		a, b = expr:find(limmsuff, 1, true)
1335	else
1336		a = a - 2
1337		local texpr = ""
1338		if (a > 1) then texpr = expr:sub(1, a - 1) end
1339		if (b < l) then texpr = texpr .. expr:sub(b + 1, l) end
1340		expr = texpr
1341	end
1342	return expr
1343end
1344
1345function escapeStr(expr)
1346	local expr2 = ""
1347	local l = string.len(expr)
1348	local c
1349	for i = 1, l do
1350		c = expr:sub(i, i)
1351		if c == "\"" then
1352			c = "\\\""
1353		end
1354		expr2 = expr2 .. c
1355	end
1356	return expr2
1357end
1358
1359function backSpaceHandler(widget)
1360	local i = 1
1361	local f = 0
1362	local n = mathmax(#histME1, #histME2)
1363	if (widget ~= fctEditor) then
1364		while (f == 0 and i <= n) do
1365			if histME1[i] == widget or histME2[i] == widget then
1366				f = i
1367			end
1368			i = i + 1
1369		end
1370	end
1371	if f > 0 then
1372		destroyD2Editor(histME1[f].editor)
1373		destroyD2Editor(histME2[f].editor)
1374		theView:remove(histME1[f])
1375		theView:remove(histME2[f])
1376		table.remove(histME1, f)
1377		table.remove(histME2, f)
1378		reposME()
1379	end
1380end
1381
1382function enterHandler(widget)
1383	local expr, exprkeep
1384	local svar
1385	local incerr = "incompatible data type"
1386	if (widget ~= fctEditor) then
1387		expr = cleanAns1(widget:getExpression())
1388		theView:setFocus(fctEditor)
1389		fctEditor:addString(expr)
1390	else
1391		if (fctEditor.editor:getExpression()) then
1392			expr = fctEditor.editor:getExpression()
1393			expr = fctEditor:getExpression()
1394			if (expr and expr ~= "") then
1395				dispinfos = false
1396				t1 = timer.getMilliSecCounter()
1397				res = luagiac.caseval(expr) or "Error"
1398				t2 = timer.getMilliSecCounter()
1399				ts  = string.format("Time :  %f" , ( t2 - t1 ) / 1000. )
1400				fctEditor.editor:setText("")
1401				fctEditor:fixContent()
1402				ioffset = 0
1403
1404				res = " " .. res
1405				expr = " " .. expr
1406				expr = expr:gsub("^%s+", " ")
1407				addME(expr, res)
1408			end
1409		end
1410	end
1411end
1412
1413function getParME(editor)
1414	for i = 1, #histME2 do
1415		if histME2[i].editor == editor then
1416			return histME1[i]
1417		end
1418	end
1419	return nil
1420end
1421
1422function getME(editor)
1423	if (fctEditor.editor == editor) then
1424		return fctEditor
1425	else
1426		for i = 1, #histME1 do
1427			if histME1[i].editor == editor then
1428				return histME1[i]
1429			end
1430		end
1431		for i = 1, #histME2 do
1432			if histME2[i].editor == editor then
1433				return histME2[i]
1434			end
1435		end
1436	end
1437	return nil
1438end
1439
1440function getMEindex(me)
1441	local ti
1442	if (fctEditor.editor == me) then
1443		return 0
1444	else
1445		ti = 0
1446		for i = #histME1, 1, -1 do
1447			if histME1[i] == me then
1448				return ti
1449			end
1450			ti = ti + 1
1451		end
1452		ti = 0
1453		for i = #histME2, 1, -1 do
1454			if histME2[i] == me then
1455				return ti
1456			end
1457			ti = ti + 1
1458		end
1459	end
1460	return 0
1461end
1462
1463function resizeMEpar(editor, w, h)
1464	local pare = getParME(editor)
1465	if pare then
1466		resizeMElim(editor, w, h, pare.w + pare.dx1 * 2)
1467	else
1468		resizeME(editor, w, h)
1469	end
1470end
1471
1472function resizeME(editor, w, h)
1473	if not editor then return end
1474	resizeMElim(editor, w, h, scrWidth / 2)
1475end
1476
1477function resizeMElim(editor, w, h, lim)
1478	if not editor then return end
1479	local met = getME(editor)
1480	if met then
1481		met.needw = w
1482		met.needh = h
1483		w = mathmax(w, 0)
1484		w = mathmin(w, scrWidth - met.dx1 * 2)
1485		h = mathmax(h, strFullHeight + 8)
1486		if (me ~= fctEditor) then
1487			w = mathmin(w, (scrWidth - lim) - 2 * met.dx1 + 1)
1488		end
1489		met:resize(w, h)
1490		needcenter = true
1491		reposME()
1492		theView:invalidate()
1493	end
1494	return editor
1495end
1496
1497ioffset = 0
1498function reposView()
1499	local focusedME = theView:getFocus()
1500	if focusedME and focusedME ~= fctEditor then
1501		local y = focusedME.y
1502		local h = focusedME.h
1503		local y0 = fctEditor.y
1504		local index = getMEindex(focusedME)
1505		if y < 0 and ioffset < index then
1506			ioffset = ioffset + 1
1507			reposME()
1508			reposView()
1509		end
1510		if y + h > y0 and ioffset > index then
1511			ioffset = ioffset - 1
1512			reposME()
1513			reposView()
1514		end
1515	end
1516end
1517
1518function reposME()
1519	local h, y, ry, res, i0, beforeh, totalh, visih, h1, h2
1520	totalh = 0
1521	beforeh = 0
1522	visih = 0
1523	fctEditor.y = scrHeight - fctEditor.h
1524	theView:repos(fctEditor)
1525	sbv:setVConstraints("justify", scrHeight - fctEditor.y + border)
1526	theView:repos(sbv)
1527	y = fctEditor.y
1528	i0 = mathmax(#histME1, #histME2)
1529	for i = i0, 1, -1 do
1530		h = 0
1531		h1 = 0
1532		h2 = 0
1533		if i <= #histME1 then h1 = mathmax(h1, histME1[i].h) end
1534		if i <= #histME2 then h2 = mathmax(h2, histME2[i].h) end
1535		h = mathmax(h1, h2)
1536		if i0 - i >= ioffset then
1537			if y >= 0 then
1538				if y >= h + border then
1539					visih = visih + h + border
1540				else
1541					visih = visih + y
1542				end
1543			end
1544			y = y - h - border
1545			ry = y
1546			totalh = totalh + h + border
1547		else
1548			ry = scrHeight
1549			beforeh = beforeh + h + border
1550			totalh = totalh + h + border
1551		end
1552		if i <= #histME1 then
1553			histME1[i].y = ry
1554			theView:repos(histME1[i])
1555			if histME1[i].focus then res = histME1[i] end
1556		end
1557		if i <= #histME2 then
1558			histME2[i].y = ry + mathmax(0, h1 - h2)
1559			theView:repos(histME2[i])
1560			if histME2[i].focus then res = histME2[i] end
1561		end
1562	end
1563	if totalh == 0 then
1564		sbv.pos = 0
1565		sbv.siz = 100
1566	else
1567		sbv.pos = beforeh * 100 / totalh
1568		sbv.siz = visih * 100 / totalh
1569	end
1570	theView:invalidate()
1571end
1572
1573function destroyD2Editor(editor)
1574	if not editor then return end
1575	editor:setVisible(false)
1576	editor:move(-10000, -10000)
1577	editor:resize(1, 1)
1578	editor = nil
1579end
1580
1581function reset()
1582	for _, v in pairs(theView.widgetList) do
1583		theView:remove(v)
1584	end
1585	for _, e in pairs(histME1) do
1586		destroyD2Editor(e.editor)
1587	end
1588	for _, e in pairs(histME2) do
1589		destroyD2Editor(e.editor)
1590	end
1591	histME1 = {}
1592	histME2 = {}
1593	fsize = 10
1594	applyFontSizeChange()
1595	platform.window:invalidate()
1596	myErrorHandler()
1597end
1598
1599function myErrorHandler(line, errMsg, callStack, locals)
1600	if errMsg then print(errMsg) end
1601	defaultFocus = nil
1602	theView = nil
1603	collectgarbage()
1604	initGUI()
1605	if errMsg then addME("Script error", errMsg) end
1606	collectgarbage()
1607	return true -- let the script continue
1608end
1609
1610platform.registerErrorHandler(myErrorHandler)
1611
1612
1613--------------------------------------------------------------------- toolpalette stuff
1614
1615function toggleBorders()
1616	showEditorsBorders = not showEditorsBorders
1617	on.resize()
1618end
1619
1620function set1d()
1621	 fctEditor.editor:setDisable2DinRT(true)
1622	on.resize()
1623end
1624
1625function set2d()
1626	 fctEditor.editor:setDisable2DinRT(false)
1627	on.resize()
1628end
1629
1630function toggleHLines()
1631	showHLines = not showHLines
1632	on.resize()
1633end
1634
1635function applyFontSizeChange()
1636	fctEditor.editor:setFontSize(fsize)
1637	for _, e in pairs(histME1) do
1638		e.editor:setFontSize(fsize)
1639	end
1640	for _, e in pairs(histME2) do
1641		e.editor:setFontSize(fsize)
1642	end
1643end
1644
1645function fontDown()
1646	fsize = fsize > 6 and (fsize - 1) or fsize
1647	applyFontSizeChange()
1648end
1649
1650function fontUp()
1651	fsize = fsize < 30 and (fsize + 1) or fsize
1652	applyFontSizeChange()
1653end
1654
1655function menustring( ch )
1656--	 theView:sendStringToFocus( ch )
1657	 if fctEditor then fctEditor:addString( ch ) end
1658end
1659
1660menu = {
1661       { "Algebra",
1662       	 { "factor(expr)",	function() menustring( "factor(" ) end },
1663       	 { "normal(expr)",	function() menustring( "normal(" ) end },
1664       	 { "simplify(expr)",	function() menustring( "simplify(" ) end },
1665       	 { "subst(expr,var,value)",	function() menustring( "subst(" ) end },
1666       	 { "convert(expr,...)",	function() menustring( "convert(" ) end },
1667       	 { "solve(expr,var)",	function() menustring( "solve(" ) end },
1668       	 { "fsolve(expr,var,guess)",	function() menustring( "fsolve(" ) end },
1669       	 { "rsolve(eq,un)",	function() menustring( "rsolve(" ) end },
1670       	 { "partfrac(expr)",	function() menustring( "partfrac(" ) end },
1671       	 { "tcollect(expr)",	function() menustring( "tcollect(" ) end },
1672       	 { "texpand(expr)",	function() menustring( "texpand(" ) end },
1673       	 { "cfactor(expr)",	function() menustring( "cfactor(" ) end },
1674       	 { "cpartfrac(expr)",	function() menustring( "cpartfrac(" ) end },
1675       	 { "csolve(expr,var)",	function() menustring( "csolve(" ) end },
1676       },
1677       { "Calculus",
1678       	 { "diff(expr,var)",	function() menustring( "diff(" ) end },
1679       	 { "int(expr,var)",	function() menustring( "int(" ) end },
1680       	 { "limit(expr,var,value,[1|-1])",	function() menustring( "limit(" ) end },
1681       	 { "series(expr,var=value,order,[1|-1])",	function() menustring( "series(" ) end },
1682       	 { "sum(expr,var,min,max)",	function() menustring( "sum(" ) end },
1683       	 { "desolve(eq,x,y)",	function() menustring( "desolve(" ) end },
1684       },
1685       { "Complex and Reals",
1686       	 { "abs(x)",	function() menustring( "abs(" ) end },
1687       	 { "floor(x)",	function() menustring( "floor(" ) end },
1688       	 { "sign(x)",	function() menustring( "sign(" ) end },
1689       	 { "ceil(x)",	function() menustring( "ceil(" ) end },
1690       	 { "max(x)",	function() menustring( "max(" ) end },
1691       	 { "min(x)",	function() menustring( "min(" ) end },
1692       	 { "round(x[,n])",	function() menustring( "round(" ) end },
1693       	 { "evalf(x[,prec])",	function() menustring( "evalf(" ) end },
1694       	 { "[inf..sup]",	function() menustring( "[..]") end },
1695       	 { "re(z)",	function() menustring( "re(" ) end },
1696       	 { "im(z)",	function() menustring( "im(" ) end },
1697       	 { "conj(z)",	function() menustring( "conj(" ) end },
1698       	 { "arg(z)",	function() menustring( "arg(" ) end },
1699       	 { "convert(expr,interval[,prec])",	function() menustring( "convert(,interval)" ) end },
1700       },
1701       { "Integers",
1702       	 { "iquo(a,b): euclidean quotient",	function() menustring( "iquo(" ) end },
1703       	 { "irem(a,b): euclidean remainder",	function() menustring( "irem(" ) end },
1704       	 { "is_prime(p)",	function() menustring( "is_prime" ) end },
1705       	 { "nextprime(n)",	function() menustring( "nextprime(" ) end },
1706       	 { "ifactor(n): factor integer",	function() menustring( "ifactor(" ) end },
1707       	 { "idivis(n): divisor list",	function() menustring( "idivis(" ) end },
1708       	 { "iegcd(a,b): a*u+b*v=gcd(a,b)",	function() menustring( "iegcd(" ) end },
1709       	 { "iabcuv(a,b,c): a*u+b*v=c",	function() menustring( "iabcuv(" ) end },
1710       	 { "euler(n): Euler indicatrix",	function() menustring( "euler(" ) end },
1711       	 { "ichrem([a,n],[b,m]): Chinese remainder",	function() menustring( "ichrem(" ) end },
1712       	 { "powmod(a,m,n): a^m mod n",	function() menustring( "powmod(" ) end },
1713       },
1714       { "Linalg",
1715       	 { "linsolve(",	function() menustring( "linsolve(" ) end },
1716       	 { "rref(M)",	function() menustring( "rref(" ) end },
1717       	 { "ref(M)",	function() menustring( "ref(" ) end },
1718       	 { "ker(M)",	function() menustring( "ker(" ) end },
1719       	 { "image(M)",	function() menustring( "image(" ) end },
1720       	 { "det(M)",	function() menustring( "det(" ) end },
1721       	 { "inv(M)",	function() menustring( "inv(" ) end },
1722       	 { "bug(M)",	function() menustring( "bug(" ) end },
1723       	 { "eigenvalues(M)",	function() menustring( "eigenvalues(" ) end },
1724       	 { "eigenvectors(M)",	function() menustring( "eigenvects(" ) end },
1725       	 { "jordan(M)",	function() menustring( "jordan(" ) end },
1726       	 { "matpow(M,n): M^n",	function() menustring( "matpow(" ) end },
1727       },
1728       { "Matrix, Vector",
1729       	 { "dot(v1,v2)",	function() menustring( "dot(" ) end },
1730       	 { "cross(v1,v2)",	function() menustring( "cross(" ) end },
1731       	 { "identity(n)",	function() menustring( "identity(" ) end },
1732       	 { "matrix(n,m,function)",	function() menustring( "matrix(" ) end },
1733       	 { "randmatrix(n,m,law,[params])",	function() menustring( "randmatrix(" ) end },
1734       	 { "hilbert(n)",	function() menustring( "hilbert(" ) end },
1735       	 { "vandermonde(list)",	function() menustring( "vandermonde(" ) end },
1736       	 { "l1norm(M)",	function() menustring( "l1norm(" ) end },
1737       	 { "l2norm(M)",	function() menustring( "l2norm(" ) end },
1738       	 { "linfnorm(M)",	function() menustring( "linfnorm(" ) end },
1739       	 { "cond(M,1|2|inf)",	function() menustring( "cond(" ) end },
1740       	 { "lu(M)",	function() menustring( "lu(" ) end },
1741       	 { "qr(M)",	function() menustring( "qr(" ) end },
1742       	 { "schur(M)",	function() menustring( "schur(" ) end },
1743       	 { "svd(M): sing. value dec.",	function() menustring( "svd(" ) end },
1744       	 { "svl(M): singular values",	function() menustring( "svl(" ) end },
1745       },
1746       { "Polynomials",
1747       	 { "horner(P,x)",	function() menustring( "horner(" ) end },
1748       	 { "factor(P)",	function() menustring( "factor(" ) end },
1749       	 { "cfactor(P): complex factorization",	function() menustring( "cfactor(" ) end },
1750       	 { "proot(P)",	function() menustring( "proot(" ) end },
1751       	 { "pcoeff(list)",	function() menustring( "pcoeff(" ) end },
1752       	 { "degree(P,var)",	function() menustring( "degree(" ) end },
1753       	 { "canonical_form(P,var)",	function() menustring( "canonical_form(" ) end },
1754       	 { "lagrange(X,Y): interpolation",	function() menustring( "lagrange(" ) end },
1755       	 { "quorem(A,B): eucl. div.",	function() menustring( "quorem(" ) end },
1756       	 { "gcd(A,B)",	function() menustring( "gcd(" ) end },
1757       	 { "egcd(A,B,var): A*U+B*V=gcd(A,B)",	function() menustring( "egcd(" ) end },
1758       	 { "abcuv(A,B,C,var): A*U+B*V=C",	function() menustring( "abcuv(" ) end },
1759       	 { "coeff(P,var,n)",	function() menustring( "coeff(" ) end },
1760       	 { "symb2poly(P,var)",	function() menustring( "symb2poly(" ) end },
1761       	 { "poly2symb(list,var)",	function() menustring( "poly2symb(" ) end },
1762       	 { "resultant(A,B,var)",	function() menustring( "resultant(" ) end },
1763       	 { "gbasis(listpoly,listvar)",	function() menustring( "gbasis(" ) end },
1764       	 { "cyclotomic(n)",	function() menustring( "cyclotomic(" ) end },
1765       	 { "hermite(n)",	function() menustring( "hermite(" ) end },
1766       	 { "tchebyshev1(n)",	function() menustring( "tchebyshev1(" ) end },
1767       	 { "tchebyshev2(n)",	function() menustring( "tchebyshev2(" ) end },
1768       	 { "randpoly(n)",	function() menustring( "randpoly(" ) end },
1769       },
1770       { "Proba",
1771       	 { "comb(n,p)",	function() menustring( "comb(" ) end },
1772       	 { "perm(n,p)",	function() menustring( "perm(" ) end },
1773       	 { "factorial(n)",	function() menustring( "factorial(" ) end },
1774       	 { "rand()",	function() menustring( "rand(" ) end },
1775       	 { "binomial(n,p,k)",	function() menustring( "binomial(" ) end },
1776       	 { "binomial_cdf(n,p,x)",	function() menustring( "binomial_cdf(" ) end },
1777       	 { "binomial_icdf(n,p,y)",	function() menustring( "binomial_icdf(" ) end },
1778       	 { "normald(m,sigma,x)",	function() menustring( "normald(" ) end },
1779       	 { "normald_cdf(m,sigma,x)",	function() menustring( "normald_cdf(" ) end },
1780       	 { "normald_icdf(m.sigma,y)",	function() menustring( "normald_icdf(" ) end },
1781       	 { "poisson(mu,k)",	function() menustring( "poisson(" ) end },
1782       	 { "exponentiald(mu,x)",	function() menustring( "exponentiald(" ) end },
1783       	 { "geometric(p,k)",	function() menustring( "geometric(" ) end },
1784       	 { "chisquared(n,x)",	function() menustring( "chisquared(" ) end },
1785       	 { "uniformd(a,b,x)",	function() menustring( "uniformd(" ) end },
1786       	 { "chisquaret(l1,l2): chi2 test",	function() menustring( "chisquaret(" ) end },
1787       	 { "normalt: Z-test",	function() menustring( "normalt(" ) end },
1788       	 { "studentt: Student-test",	function() menustring( "studentt(" ) end },
1789       },
1790       { "Program",
1791       	 { ";",	function() menustring( ";\n\r" ) end },
1792       	 { "function",	function() menustring( "f(x):={ local y; y:=x*x; return y; }" ) end },
1793       	 { "local",	function() menustring( "local ;\n" ) end },
1794       	 { "return",	function() menustring( "return ;" ) end },
1795       	 { "print",	function() menustring( "print(" ) end },
1796       	 { "if/then",	function() menustring( "if then ; fi;" ) end },
1797       	 { "if/then/else",	function() menustring( "if then ; else ; fi;" ) end },
1798       	 { "for",	function() menustring( "for j from 1 to 5 do ; od;" ) end },
1799       	 { "while",	function() menustring( "while do ; od;" ) end },
1800       	 { "repeat",	function() menustring( "repeat ; until ;" ) end },
1801       	 { "break",	function() menustring( "break;" ) end },
1802       	 { "continue",	function() menustring( "continue;" ) end },
1803       },
1804       { "Lists",
1805       	 { "seq(expr,var,inf,sup)",	function() menustring( "seq(" ) end },
1806       	 { "size(l)",	function() menustring( "size(" ) end },
1807       	 { "op(l): list to sequence",	function() menustring( "op(" ) end },
1808       	 { "apply(f,l) apply function",	function() menustring( "apply(" ) end },
1809       	 { "append(l,expr)",	function() menustring( "append(" ) end },
1810       	 { "concat(l1,l2)",	function() menustring( "concat(" ) end },
1811       	 { "head(l): first element",	function() menustring( "head(" ) end },
1812       	 { "tail(l)",	function() menustring( "tail(" ) end },
1813       	 { "sort(l[,function])",	function() menustring( "sort(" ) end },
1814       	 { "revlist(l)",	function() menustring( "revlist(" ) end },
1815       	 { "contains(l,expr)",	function() menustring( "contains(" ) end },
1816       	 { "suppress(l,n)",	function() menustring( "suppress(" ) end },
1817       	 { "remove(function,l)",	function() menustring( "remove(" ) end },
1818       },
1819	{
1820		"Options",
1821		{ "save variables",	function() menustring( "write(\"a.tns\",0" ) end },
1822		{ "read variables",	function() menustring( "eval(read(\"a.tns\"))" ) end },
1823		"-",
1824		-- { "1d", set1d },
1825		-- { "2d", set2d },
1826		{ "Show/hide editors borders", toggleBorders },
1827		{ "Show/hide horizontal lines", toggleHLines },
1828		"-",
1829		{ "Increase font size", fontUp },
1830		{ "Decrease font size", fontDown },
1831		"-",
1832		{ "Restart CAS",	function() menustring( "restart;" ) end },
1833		{ "Clear history", reset }
1834	}
1835}
1836toolpalette.register(menu)
1837