-- card: 4702 from stack: in -- bmap block id: 4446 -- flags: 0000 -- background id: 2619 -- name: Card 5 ----- HyperTalk script ----- on opencard get rect of card button id 1 BarButton 3,600,0,500 end opencard -- part 1 (button) -- low flags: 00 -- high flags: 0002 -- rect: left=372 top=139 right=222 bottom=453 -- title width / last selected line: 0 -- icon id / first selected line: 0 / 0 -- text alignment: 1 -- font id: 0 -- text size: 24 -- style flags: 0 -- line height: 32 -- part name: ----- HyperTalk script ----- on mousedown if the optionkey is down then put script of me into bkgnd field lowerpane set cursor to 2 get the rect of me BarButton 3,600,0 put the result into bkgnd field results end mousedown -- part 2 (button) -- low flags: 00 -- high flags: 8003 -- rect: left=174 top=296 right=325 bottom=313 -- title width / last selected line: 0 -- icon id / first selected line: 0 / 0 -- text alignment: 1 -- font id: 0 -- text size: 12 -- style flags: 0 -- line height: 16 -- part name: Show Card's script ----- HyperTalk script ----- on mouseUp put the script of this card into bkgnd field lowerpane end mouseUp on mousedown if the optionkey is down then put script of me into bkgnd field lowerpane end mousedown -- part 8 (field) -- low flags: 80 -- high flags: 0000 -- rect: left=312 top=57 right=142 bottom=512 -- title width / last selected line: 0 -- icon id / first selected line: 0 / 0 -- text alignment: 0 -- font id: 3 -- text size: 9 -- style flags: 0 -- line height: 12 -- part name: -- part contents for background part 10 ----- text ----- You can issue the "BarButton" command at any time; you're not limited to just when a user clicks in the button. The fourth parameter is used when you wish to force an initial value without requiring that the user click in the button. When you supply this fourth parameter, BarButton displays the initial value and immediately returns, without even looking at the mouse location. Throughout this stack we have assigned initial values to the Bar buttons in the script of each card, in response to the "opencard" event. You can easily look at an example of this by clicking on the button "Show Card's script". Or you can simulate an opencard event by "blind typing" the "opencard" command. In some circumstances you may wish to graph initial values using the fourth parameter, but not want the numeric value displayed. A first parameter value less than zero tells BarButton to do this. So instead of using values between 1 and 4, use the corresponding negatives between -1 and -4: -1: left to right, no numeric display -2: right to left, no numeric display -3: bottom to top, no numeric display -4: top to bottom, no numeric display The script of the first card in this stack has several examples of this technique. -- part contents for background part 4 ----- text ----- 422 -- part contents for card part 8 ----- text ----- {BarButton adjusts the size of the bar contained within rect in first 4} {parameters.} UNIT Main; INTERFACE USES HyperXCMD; PROCEDURE Main (ParamPtr : XCmdPtr); IMPLEMENTATION {-------------------------------------------------------------------------------} PROCEDURE BarButton (ParamPtr : XCmdPtr); FORWARD; PROCEDURE Main; BEGIN BarButton(ParamPtr); END; PROCEDURE BarButton; CONST leftToRight = 1; rightToLeft = 2; bottomToTop = 3; topToBottom = 4; VAR str : Str255; ButtonRect, DrawRect, BlankRect, ViewRect, TextRect : rect; InRect, newLoc, dummy, ShowValue, SuppressDisplay : boolean; spot, oldspot, TextPoint : point; direction, maximumValue, minimumValue, CurrentValue, InitialValue : integer; theInfo : FontInfo; TextHeight, TextWidth, ButtonHeight, ButtonWidth : integer; proportion : real; {-------------------------------------------------------------------------------} PROCEDURE DoJsr (addr : ProcPtr); INLINE $205F, $4E90; { Jump subroutine to a procedure. Pop address into A0, JSR (A0) } {-------------------------------------------------------------------------------} PROCEDURE ZeroToPas (zeroStr : Ptr; VAR pasStr : Str255); {Fill the Pascal string with the contents of the zero-terminated} { string. You create the Pascal string and pass it in as a VAR } { parameter. Useful for converting the arguments of any XCMD to } { Pascal strings.} BEGIN WITH paramPtr^ DO BEGIN inArgs[1] := ORD(zeroStr); inArgs[2] := ORD(@pasStr); request := xreqZeroToPas; DoJsr(entryPoint); END; END; {-------------------------------------------------------------------------------} FUNCTION StrToNum (str : Str31) : LongInt; { Convert a string of ASCII decimal digits to a signed long integer.} { Negative sign is allowed. } BEGIN WITH paramPtr^ DO BEGIN inArgs[1] := ORD(@str); request := xreqStrToNum; DoJsr(entryPoint); StrToNum := outArgs[1]; END; END; {-------------------------------------------------------------------------------} FUNCTION PasToZero (str : Str255) : Handle; { Convert a Pascal string to a zero-terminated string. Returns a handle} { to a new zero-terminated string. The caller must dispose the handle. } BEGIN WITH paramPtr^ DO BEGIN inArgs[1] := ORD(@str); request := xreqPasToZero; DoJsr(entryPoint); PasToZero := Handle(outArgs[1]); END; END; {-------------------------------------------------------------------------------} FUNCTION EvalExpr (expr : Str255) : Handle; { Evaluate a HyperCard expression and return the answer. The answer is} { a handle to a zero-terminated string. } BEGIN WITH paramPtr^ DO BEGIN inArgs[1] := ORD(@expr); request := xreqEvalExpr; DoJsr(entryPoint); EvalExpr := Handle(outArgs[1]); END; END; {-------------------------------------------------------------------------------} FUNCTION NumToStr (num : LongInt) : Str31; { Convert a signed long integer to a Pascal string. } VAR str : Str31; BEGIN WITH paramPtr^ DO BEGIN inArgs[1] := num; inArgs[2] := ORD(@str); request := xreqNumToStr; DoJsr(entryPoint); NumToStr := str; END; END; {-------------------------------------------------------------------------------} FUNCTION IntExpr (theString : str255) : integer; TYPE Ptr31 = ^str31; Hand31 = ^Ptr31; VAR tempHand : handle; ShortStr : str31; ShortPtr : Ptr31; ShortHand : Hand31; NumLong : longint; LongStr : str255; BEGIN tempHand := EvalExpr(theString); ZeroToPas(tempHand^, LongStr); ShortStr := LongStr; NumLong := StrToNum(ShortStr); IntExpr := loword(NumLong); DisposHandle(tempHand); END; {-------------------------------------------------------------------------------} FUNCTION DePad (str : str255) : str255; VAR c : integer; BEGIN FOR c := length(str) DOWNTO 1 DO IF ord(str[c]) = 32 THEN Delete(str, c, 1); DePad := str; END; {-------------------------------------------------------------------------------} PROCEDURE DisplayValue; BEGIN IF ShowValue THEN BEGIN TextFont(systemfont);{Chicago} TextSize(12); str := Depad(StringOf(CurrentValue)); CASE direction OF leftToRight, rightToLeft : MoveTo(TextPoint.h, TextPoint.v); bottomToTop, topToBottom : MoveTo(TextPoint.h - StringWidth(str) DIV 2, TextPoint.v); END; EraseRect(TextRect); DrawString(str); END; END;{DisplayValue} {-------------------------------------------------------------------------------} PROCEDURE ComputeValue; BEGIN WITH ViewRect DO CASE direction OF leftToRight : proportion := (oldspot.h - left) * 1.0 / (right - left); rightToLeft : proportion := (right - oldspot.h) * 1.0 / (right - left); bottomToTop : proportion := (bottom - oldspot.v) * 1.0 / (bottom - top); topToBottom : proportion := (oldspot.v - top) * 1.0 / (bottom - top); END; IF proportion < 0 THEN proportion := 0; IF proportion > 1.0 THEN proportion := 1.0; CurrentValue := round(proportion * (maximumValue - minimumValue) + minimumValue); DisplayValue; END;{ComputeValue} {-------------------------------------------------------------------------------} BEGIN ButtonRect.left := IntExpr('item 1 of It'); ButtonRect.top := IntExpr('item 2 of It'); ButtonRect.right := IntExpr('item 3 of It'); ButtonRect.bottom := IntExpr('item 4 of It'); ButtonHeight := ButtonRect.bottom - ButtonRect.top; ButtonWidth := ButtonRect.right - ButtonRect.left; ViewRect := ButtonRect; InsetRect(ViewRect, 1, 1); DrawRect := ViewRect; BlankRect := DrawRect; WITH ButtonRect DO IF right - left > bottom - top THEN direction := leftToRight ELSE direction := bottomToTop; maximumValue := 100; minimumValue := 0; ShowValue := FALSE; SuppressDisplay := FALSE; InitialValue := -32767;{Hopefully no one will actually use that value!} WITH ParamPtr^ DO BEGIN IF paramCount > 0 THEN BEGIN ZeroToPas(params[1]^, str); direction := StrToNum(str); IF (direction >= -4) AND (direction <= -1) THEN BEGIN SuppressDisplay := TRUE; direction := -direction; END; IF (direction < 1) OR (direction > 4) THEN direction := leftToRight; IF paramCount > 1 THEN BEGIN ZeroToPas(params[2]^, str); maximumValue := StrToNum(str); ShowValue := TRUE; IF paramCount > 2 THEN BEGIN ZeroToPas(params[3]^, str); minimumValue := StrToNum(str); IF minimumValue >= maximumValue THEN minimumValue := maximumValue + 10; IF paramCount > 3 THEN BEGIN ZeroToPas(params[4]^, str); InitialValue := StrToNum(str); END;{end of reading initial value} END;{end of reading third parameter} END;{end of reading second parameter} END;{end of reading first parameter} END;{with ParamPtr^} IF SuppressDisplay THEN ShowValue := FALSE; IF InitialValue = -32767 THEN GetMouse(spot){Get current loc of mouse} ELSE BEGIN{force initial value of bar without mousedown} proportion := (InitialValue - minimumValue) * 1.0 / (maximumValue - minimumValue); IF proportion < 0 THEN proportion := 0; IF proportion > 1.0 THEN proportion := 1.0; CASE direction OF leftToRight : spot.h := round(DrawRect.left + proportion * ButtonWidth); rightToLeft : spot.h := round(DrawRect.right - proportion * ButtonWidth); bottomToTop : spot.v := round(DrawRect.bottom - proportion * ButtonHeight); topToBottom : spot.v := round(DrawRect.top + proportion * ButtonHeight); END;{end of case direction} END;{end of forcing initial value} CASE direction OF leftToRight : BEGIN DrawRect.right := spot.h; BlankRect.left := spot.h; END; rightToLeft : BEGIN DrawRect.left := spot.h; BlankRect.right := spot.h; END; bottomToTop : BEGIN DrawRect.top := spot.v; BlankRect.bottom := spot.v; END; topToBottom : BEGIN DrawRect.bottom := spot.v; BlankRect.top := spot.v; END; END;{end of case direction} IF ShowValue THEN{Set up stuff for ShowValue} BEGIN IF StringWidth(DePad(StringOf(maximumValue))) >= StringWidth(DePad(StringOf(minimumValue))) THEN TextWidth := StringWidth(DePad(StringOf(maximumValue))) ELSE TextWidth := StringWidth(DePad(StringOf(minimumValue))); GetFontInfo(theInfo); TextHeight := theInfo.ascent;{numbers don't have descenders} CASE direction OF leftToRight, rightToLeft : BEGIN TextRect.top := ButtonRect.top + ButtonHeight DIV 2 - TextHeight DIV 2 - TextHeight DIV 4; TextRect.left := ButtonRect.right + 1; TextPoint.h := TextRect.left + TextHeight DIV 4; TextRect.bottom := TextRect.top + TextHeight + TextHeight DIV 2; TextPoint.v := TextRect.bottom - TextHeight DIV 4; END; bottomToTop, topToBottom : BEGIN TextRect.top := ButtonRect.bottom + 2; TextPoint.h := ButtonRect.left + ButtonWidth DIV 2; TextRect.left := TextPoint.h - TextWidth DIV 2 - TextHeight DIV 4; TextRect.bottom := TextRect.top + TextHeight + TextHeight DIV 2 - 4; TextPoint.v := TextRect.bottom - TextHeight DIV 4; END; END; TextRect.right := TextRect.left + TextWidth + TextHeight DIV 2; END;{end setting up ShowValue stuff} oldspot := spot; dummy := SectRect(ViewRect, DrawRect, DrawRect); PaintRect(DrawRect); dummy := SectRect(ViewRect, BlankRect, BlankRect); EraseRect(BlankRect); DrawRect := ViewRect;{now will be used for the redrawing} InsetRect(ButtonRect, -10, -10); IF InitialValue > -32767 THEN BEGIN{Display forced starting value} CurrentValue := InitialValue; DisplayValue; END ELSE BEGIN ComputeValue;{Display first real value if requested} REPEAT GetMouse(spot); InRect := PtInRect(spot, ButtonRect) AND Button; IF InRect THEN BEGIN newLoc := TRUE;{guilty unless proven innocent} CASE direction OF leftToRight, rightToLeft : BEGIN IF spot.h = oldspot.h THEN{no horizontal movement; don't redraw at all} newLoc := FALSE ELSE BEGIN IF spot.h > oldspot.h THEN{moving right} BEGIN DrawRect.right := spot.h; DrawRect.left := oldspot.h; END ELSE{moving left} BEGIN DrawRect.left := spot.h; DrawRect.right := oldspot.h; END; END;{end new horizontal loc} DrawRect.top := ViewRect.top;{restore top and bottom in case SectRect wiped them out with empty rect} DrawRect.bottom := ViewRect.bottom; END;{end horizontal case} bottomToTop, topToBottom : BEGIN IF spot.v = oldspot.v THEN{no vertical movement; don't redraw at all} newLoc := FALSE ELSE BEGIN IF spot.v > oldspot.v THEN{moving down} BEGIN DrawRect.bottom := spot.v; DrawRect.top := oldspot.v; END ELSE{moving up} BEGIN DrawRect.top := spot.v; DrawRect.bottom := oldspot.v; END; END;{end new vertical loc} DrawRect.left := ViewRect.left;{restore left and right in case SectRect wiped them out with empty rect} DrawRect.right := ViewRect.right; END;{end vertical case} END;{case block} dummy := SectRect(ViewRect, DrawRect, DrawRect); IF newLoc THEN{Redraw if necessary} BEGIN InvertRect(DrawRect); IF ShowValue THEN ComputeValue;{Display current value if requested} END; oldspot := spot;{today becomes yesterday} END;{end InRect} UNTIL NOT Button;{loop until mouseUp} ComputeValue;{Put result in "the Result" even if they don't want it displayed} END;{end of event loop for real values} str := NumToStr(CurrentValue); paramPtr^.returnValue := PasToZero(str); END;{end BarButton} END.