home *** CD-ROM | disk | FTP | other *** search
- /*
- This file contains a demo of the use of attributes to implement text buttons.
-
-
-
- Starting the Demo.
- Simply linking EPMCALC will start this demo.
- It is a simple calculator that only handles
- positive numbers less than 65536. It uses reverse polish notation just like
- Hewlett Packard calculators. The buttons are activated by placing the cursor
- over the button to be pressed and invoking the ExecuteCursor command.
- (ExecuteCursor can be found in the BUTTONS.E file.) The meaning of most
- of the buttons is obvious, but the button with the < character in it is
- a delete character.
- I recommend that ExecuteCursor be bound to a keystroke or mouse action
- to improve the useablility of this calculator.
-
- How is it done.
- This calculator is done by simply using the functions provided in
- BUTTONS.E to create "buttons" in the text of the document linked to
- commands defined in this file. For information about how buttons are
- implemented browse the buttons.e file.
-
-
- The following paragraph is no longer true, but it remains here for
- illustrative purposes. The enhancement it suggests has been
- implemented.
-
- The calculator stack is kept as a bunch of
- attribute records placed near the top left corner
- of the calculator. As presently implmented the
- value field of each of these attribute records
- represents a value within the calculator stack.
- Since the value field of attribute records can only
- contain values in the range 0..65535, our
- calculator can only deal with values in this range.
- A good enhancement to this calulator would be to
- use the associated text attribute class to store
- arbitrary values on the calculator heap.
-
- The button functions of this calculator are not bound to any
- particular calculator stack. This means that a user could use the block
- copy functions of E to duplicate the calculator. If several calculators
- exist in the file, then the button action functions look for the nearest
- calculator stack and perform their action on that stack. This means that
- a user could use E's block copy function to move the keys around on the
- calculator face... and even move the buttons any place in the document.
- (Don't ask me why? But that is a neat Gee Whiz thing to do in demos.)
-
-
-
- */
-
- /*
- FindCalc
-
- finds the location of the calculator's stack. The
- calculator's stack consist's of a sequence of
- CALCSTACK_CLASS attributes. (See ---- for the
- allocation of this class.) If multiple calculators
- exist, then the one closest to the cursor location
- is returned.
- */
- /* return true if found one */
- defproc FindCalc(var BestLine, var BestColm, var BestFile)
- universal CALCSTACK_CLASS
- universal FIND_NEXT_ATTR_SUBOP
- universal FIND_PREV_ATTR_SUBOP
- VeryLarge1 = 1000000000
- OldLine = .line; OldColm = .col; getfileid OldFileId
- BestFit = VeryLarge1
- TheOffset = 0
- TheColm = OldColm
- TheLine = OldLine
- TheFile = OldFileId
- TheClass = CALCSTACK_CLASS
- attribute_action FIND_NEXT_ATTR_SUBOP, TheClass, TheOffset, TheColm, TheLine
- while (TheClass<>0) do
- Distance = ((TheLine-OldLine)*(TheLine-OldLine))
- if TheColm>OldColm then Distance = Distance + ((TheColm-OldColm)*(TheColm-OldColm))
- else Distance = Distance + ((OldColm-TheColm)*(OldColm-TheColm))
- endif
- if Distance<BestFit then
- BestLine = TheLine; BestColm = TheColm; getfileid BestFileId; BestFit=Distance
- endif
- TheColm = TheColm+1
- TheOffset = 0
- TheClass = CALCSTACK_CLASS
- attribute_action FIND_NEXT_ATTR_SUBOP, TheClass, TheOffset, TheColm, TheLine
- endwhile
- TheOffset = 0
- TheColm = OldColm
- TheLine = OldLine
- TheFile = OldFileId
- TheClass = CALCSTACK_CLASS
- attribute_action FIND_PREV_ATTR_SUBOP, TheClass, TheOffset, TheColm, TheLine
- while (TheClass<>0) do
- Distance = ((OldLine-TheLine)*(OldLine-TheLine))
- if TheColm>OldColm then Distance = Distance + ((TheColm-OldColm)*(TheColm-OldColm))
- else Distance = Distance + ((OldColm-TheColm)*(OldColm-TheColm))
- endif
- if Distance<BestFit then
- BestLine = TheLine; BestColm = TheColm; getfileid BestFileId; BestFit=Distance
- endif
- TheColm = TheColm
- TheOffset = -300
- TheClass = CALCSTACK_CLASS
- attribute_action FIND_PREV_ATTR_SUBOP, TheClass, TheOffset, TheColm, TheLine
- endwhile
- return BestFit<>VeryLarge1
-
- /*
- CalcPop
-
- This proc pops an entry off of the calculator's stack
- and returns that value.
-
- */
- defproc CalcPop(CalcLine, CalcColm, CalcFileId)
- universal CALCSTACK_CLASS
- universal FIND_PREV_ATTR_SUBOP
- universal DELETE_ATTR_SUBOP
- universal ASSOCCLASS
- getfileid OldFileid
- activatefile CalcFileid
- call psave_pos(OldPos)
- -- check for errors
- TheOffset = 0
- TheColm = CalcColm
- TheLine = CalcLine
- TheFile = CalcFileId
- TheClass = CALCSTACK_CLASS
- attribute_action FIND_PREV_ATTR_SUBOP, TheClass, TheOffset, TheColm, TheLine
- if (TheClass==0) or (TheColm<>CalcColm) or (TheLine<>CalcLine) or (TheOffset<>-2) then
- call prestore_pos(OldPos)
- return 0
- endif
- -- get value at top of stack
- CalcLine
- .col = CalcColm
- TheValue = Associated_Phrase(CALCSTACK_CLASS)
- -- delete the top of the calc stack
- TheOffset = -1
- attribute_action DELETE_ATTR_SUBOP, TheClass, TheOffset, TheColm, TheLine -- delete association
- attribute_action DELETE_ATTR_SUBOP, TheClass, TheOffset, TheColm, TheLine -- delete push calc
- TheOffset = 1
- attribute_action DELETE_ATTR_SUBOP, TheClass, TheOffset, TheColm, TheLine -- delete pop calc
- call prestore_pos(OldPos)
- return TheValue
-
- /*
- CalcPush
- Pushs the given value onto the calculator's stack
- at a given location.
- */
- defproc CalcPush(ThePhrase, CalcLine, CalcColm, CalcFileId)
- universal CALCSTACK_CLASS
- universal ASSOCCLASS
- TheValue = StashPhrase(ThePhrase)
- insert_attribute CALCSTACK_CLASS, TheValue, /*ispush:=push*/ 1, -1, CalcColm, CalcLine, CalcFileId
- insert_attribute CALCSTACK_CLASS, TheValue, /*ispush:=pop */ 0, 1, CalcColm, CalcLine, CalcFileId
- insert_attribute ASSOCCLASS, TheValue, /*ispush:=tag */ 2, -1, CalcColm, CalcLine, CalcFileId
-
- /*
- UpdateCalcScreen
-
- Draw the value at the top of the calculator's stack
- onto the screen of the calculator.
- */
- defproc UpdateCalcScreen(CalcLine, CalcColm, CalcFileId)
- universal CALCSTACK_CLASS
- universal FIND_NEXT_ATTR_SUBOP
- universal FIND_PREV_ATTR_SUBOP
- compile if EVERSION < 5
- cursordata
- compile endif
- getfileid OldFileid
- activatefile CalcFileid
- call psave_pos(OldPos)
- CalcLine
- .col = CalcColm
- TheValue = Associated_phrase(CALCSTACK_CLASS)
- .col = CalcColm + 3
- CalcLine
- if insertstate() then
- inserttoggle
- endif
- keyin substr(TheValue, 1, 15)
- call prestore_pos(OldPos)
- activatefile OldFileid
-
-
- /*
- StartCalc
-
- Build a calculator from scratch.
- */
- defc StartCalc
- universal CALCSTACK_CLASS
- universal ASSOCCLASS
- universal BUTTONCLASS
- "e /c jcalcxxx.tst"
- getfileid CalcFileid
- .filename = "EPM Calculator Demo"
- .autosave = 0
- insertline "╔══════════════════════╗", 1,CalcFileid
- insertline "║-| 5 |-║", 2,CalcFileid
- insertline "║EPM ┌─┐┌─┐┌─┐┌─┐ ║", 3,CalcFileid
- insertline "║Calc │0││1││2││3│ ║", 4,CalcFileid
- insertline "║ ┌───┐└─┘└─┘└─┘└─┘ ║", 5,CalcFileid
- insertline "║ ┌─┐│ E │┌─┐┌─┐┌─┐┌─┐ ║", 6,CalcFileid
- insertline "║ │+││ N ││4││5││6││7│ ║", 7,CalcFileid
- insertline "║ └─┘│ T │└─┘└─┘└─┘└─┘ ║", 8,CalcFileid
- insertline "║ ┌─┐│ E │┌─┐┌─┐┌─┐┌─┐ ║", 9,CalcFileid
- insertline "║ │-││ R ││8││9││.││<│ ║",10,CalcFileid
- insertline "║ └─┘└───┘└─┘└─┘└─┘└─┘ ║",11,CalcFileid
- insertline "╚══════════════════════╝",12,CalcFileid
- insertline "Use alt-dblclick-button1",13,CalcFileid
-
- insert_attribute CALCSTACK_CLASS, 0, 1/*ispush*/, -1, /*col:*/2, /*line:*/ 2 ;
- insert_attribute ASSOCCLASS, StashPhrase("0"), 2/*ispush*/, -1, /*col:*/2, /*line:*/ 2 ;
- insert_attribute CALCSTACK_CLASS, 0, 0/*ispush*/, 1, /*col:*/2, /*line:*/ 2 ;
-
- call pset_mark(1, 12, 1, 24, "BLOCK", CalcFileid)
- "associate_phrase_with_mark "BUTTONCLASS" CalcKeyed HELP"
-
- call pset_mark(6, 8, 3, 5, "BLOCK", CalcFileid)
- "associate_phrase_with_mark "BUTTONCLASS" CalcKeyed +"
- call pset_mark(9, 11, 3, 5, "BLOCK", CalcFileid)
- "associate_phrase_with_mark "BUTTONCLASS" CalcKeyed -"
- call pset_mark(5, 11, 6, 10, "BLOCK", CalcFileid)
- "associate_phrase_with_mark "BUTTONCLASS" CalcKeyed ENTER"
-
- call pset_mark(3, 5, 11, 13, "BLOCK", CalcFileid)
- "associate_phrase_with_mark "BUTTONCLASS" CalcKeyed 0"
- call pset_mark(3, 5, 14, 16, "BLOCK", CalcFileid)
- "associate_phrase_with_mark "BUTTONCLASS" CalcKeyed 1"
- call pset_mark(3, 5, 17, 19, "BLOCK", CalcFileid)
- "associate_phrase_with_mark "BUTTONCLASS" CalcKeyed 2"
- call pset_mark(3, 5, 20, 22, "BLOCK", CalcFileid)
- "associate_phrase_with_mark "BUTTONCLASS" CalcKeyed 3"
-
- call pset_mark(6, 8, 11, 13, "BLOCK", CalcFileid)
- "associate_phrase_with_mark "BUTTONCLASS" CalcKeyed 4"
- call pset_mark(6, 8, 14, 16, "BLOCK", CalcFileid)
- "associate_phrase_with_mark "BUTTONCLASS" CalcKeyed 5"
- call pset_mark(6, 8, 17, 19, "BLOCK", CalcFileid)
- "associate_phrase_with_mark "BUTTONCLASS" CalcKeyed 6"
- call pset_mark(6, 8, 20, 22, "BLOCK", CalcFileid)
- "associate_phrase_with_mark "BUTTONCLASS" CalcKeyed 7"
-
- call pset_mark(9, 11, 11, 13, "BLOCK", CalcFileid)
- "associate_phrase_with_mark "BUTTONCLASS" CalcKeyed 8"
- call pset_mark(9, 11, 14, 16, "BLOCK", CalcFileid)
- "associate_phrase_with_mark "BUTTONCLASS" CalcKeyed 9"
- call pset_mark(9, 11, 17, 19, "BLOCK", CalcFileid)
- "associate_phrase_with_mark "BUTTONCLASS" CalcKeyed ."
- call pset_mark(9, 11, 20, 22, "BLOCK", CalcFileid)
- "associate_phrase_with_mark "BUTTONCLASS" CalcKeyed <"
- unmark
-
- call UpdateCalcScreen(2, 2, CalcFileId)
- call prestore_pos("4 12 12 4"); -- .line .col .cursorx .cursory
- "enable_attr_keys"
-
-
- /*
- CalcKeyed
-
- The arithmetic guts of the calculator. It takes its
- argument, applies the argument to the calculator stack,
- and redisplays the screen of the calculator. This
- command is generally invoked by pressing calculator
- buttons, but it can also be called from the command
- line.
- */
- defc CalcKeyed
- compile if EVERSION < 5
- cursordata
- compile endif
- call psave_pos(OldPos)
- CalcLine = .line; CalcCol = .col; getfileid CalcFile
- FoundCalc = FindCalc(CalcLine, CalcCol, CalcFile)
- if (FoundCalc==0) and (arg(1)<>"HELP") then
- call showmessage(" Sorry, the calculator could not be found.")
- return
- else
- endif
- if arg(1)=="ENTER" then
- call CalcPush(0, CalcLine, CalcCol, CalcFile)
- elseif arg(1)=="HELP" then
- call showmessage(" EPM Calc Help: ",
- " This is a trivial reverse-Polish ",
- " notation calculator. Just ",
- " alt-doubleclick with button 1. ")
- elseif arg(1)=="+" then
- Arg1 = CalcPop(CalcLine, CalcCol, CalcFile)
- Arg2 = CalcPop(CalcLine, CalcCol, CalcFile)
- call CalcPush(Arg1+Arg2, CalcLine, CalcCol, CalcFile)
- elseif arg(1)=="-" then
- Arg1 = CalcPop(CalcLine, CalcCol, CalcFile)
- Arg2 = CalcPop(CalcLine, CalcCol, CalcFile)
- call CalcPush(Arg2-Arg1, CalcLine, CalcCol, CalcFile)
- elseif arg(1)=="/" then
- Arg1 = CalcPop(CalcLine, CalcCol, CalcFile)
- if Arg1=0 then
- call messagenwait("Division by zero not supported.")
- call CalcPush(Arg1, CalcLine, CalcCol, CalcFile)
- else
- Arg2 = CalcPop(CalcLine, CalcCol, CalcFile)
- call CalcPush(Arg2/Arg1, CalcLine, CalcCol, CalcFile)
- endif
- elseif arg(1)=="%" then
- Arg1 = CalcPop(CalcLine, CalcCol, CalcFile)
- if Arg1=0 then
- call messagenwait("Division by zero not supported.")
- call CalcPush(Arg1, CalcLine, CalcCol, CalcFile)
- else
- Arg2 = CalcPop(CalcLine, CalcCol, CalcFile)
- call CalcPush(Arg2%Arg1, CalcLine, CalcCol, CalcFile)
- endif
- elseif arg(1)=="//" then
- Arg1 = CalcPop(CalcLine, CalcCol, CalcFile)
- if Arg1=0 then
- call messagenwait("Division by zero not supported.")
- call CalcPush(Arg1, CalcLine, CalcCol, CalcFile)
- else
- Arg2 = CalcPop(CalcLine, CalcCol, CalcFile)
- call CalcPush(Arg2//Arg1, CalcLine, CalcCol, CalcFile)
- endif
- elseif (arg(1)=="+-") or (arg(1)=="-+") then
- Arg1 = CalcPop(CalcLine, CalcCol, CalcFile)
- call CalcPush(-1 * Arg1, CalcLine, CalcCol, CalcFile)
- elseif arg(1)=="." then
- call showmessage(" Sorry, non natural numbers are not yet officially supported.");
- elseif arg(1)=="<" then
- Arg1 = CalcPop(CalcLine, CalcCol, CalcFile)
- call CalcPush(Arg1%10, CalcLine, CalcCol, CalcFile)
- elseif (arg(1)>="0") and (arg(1)<="9") then
- Arg1 = CalcPop(CalcLine, CalcCol, CalcFile)
- Result = ( Arg1||arg(1) ) + 0 -- handles zero, positive and negatives.
- call CalcPush(Result, CalcLine, CalcCol, CalcFile)
- else
- call showmessage(" Sorry, I don't understand that key.")
- endif
- call UpdateCalcScreen(CalcLine, CalcCol, CalcFile)
- call prestore_pos(OldPos)
-
-
- definit
- sayerror "bozoxo1"
- "link buttons"
- sayerror "bozoxo2"
- "startcalc"
-