HyperActive Software

Home What's New Who We Are What We Do Solutions Resources

We make software for humans. Custom Mac, Windows, iOS and Android solutions in HyperCard, MetaCard, and RunRev LiveCode

Resources...


HyperCard Tips and Tricks: Scripting Tricks

Lambda functions

Posted: 9/13/00
A simple function can be defined and executed programmatically using a
specially reserved object to store the handler. This is similar to the
lambda declaration used in Lisp.

Example Implementation: 

(1) Create a background button for storing the in-the-fly 
    function handler.
(2) Define a command-that-defines-the-function.
    The first line of the argument to the defining command 
    is a comma-delimited list of data parameters (or empty)
    and the rest of the lines are the expressions to evaluate.
    The last line is the expression to evaluate and return.
    The function-that-defines-the-function should insert
    "function" and a generic name before the parameter list,
    a global variable definition for storing the final result,
    and append the intermediate forms, a final assignment
    statement for storing the value of the last form, and a closing
    "end " and the generic function name.
(3) Define a generic calling function name that references
    the function stored in the button created in Step (1).
    This function should call the stored function and retrieve
    the result from the global variable.
(4) Define and store the function using the defining-function from
    Step (2) and call the stored function with the generic
    named function in Step (3).

Example implementation:

Create bkgnd btn "*LambdaFunction*"
        
on defLambda paramsAndForms
  global LambdaResult
  if (there is a bkgnd btn "*LambdaFunction*") then
    put (line 1 of paramsAndForms) into paramNames
    put (line 2 to (the number of lines in paramsAndForms - 1) ¬
of paramsAndForms) into forms set the script of bkgnd btn "*LambdaFunction*" to ¬ "on lambda"&&paramNames&RETURN&"global LambdaResult"&RETURN ¬ &forms&RETURN ¬ &"put"&&(last line of paramsAndForms)&&"into LambdaResult"&RETURN ¬ &"end lambda" end if put EMPTY into LambdaResult end defLambda function lambda global LambdaResult delete last char of paramList if (there is a bkgnd btn "*LambdaFunction*") then put "lambda " into lambdaMsg repeat with i = 1 to (the paramCount) put param(i)&"," after lambdaMsg end repeat send lambdaMsg to bkgnd btn "*LambdaFunction*" put LambdaResult into temp put EMPTY into LambdaResult return temp else return "*ERROR*" end if end lambda defLambda " "&RETURN&"This is a test" put lambda() ==> This is a test.

Steve Long



Keeping fields editable on idle

Posted: 2/7/00
When you trap the "idle" system message, your normally editable fields
don't work anymore. Here is a way around it.

In the stack script, enter this:

on openfield
  global KillIdleProcess
  put true into KillIdleProcess
end openfield

on closefield
  global KillIdleProcess
  put false into KillIdleProcess
end closefield

on exitfield
  global KillIdleProcess
  put false into KillIdleProcess
end exitfield

At the beginning of your idle handler, put this:

on idle
  global KillIdleProcess
  if KillIdleProcess is empty then put false into KillIdleProcess
  if KillIdleProcess then exit idle
  -- continue with your script

end idle

These scripts will set a flag when you click in a field. When the flag
is set, the idle handler is exited so there is no interaction with
fields to deselect the text you're editing. When you are finished
editing, the flag is reset to again let the idle handler do it's work.

Jon Bettencourt



Get card position in background

Posted: 7/26/99
This function returns the position of the current card in its
background, i.e. that this is the 5th card of this background. Note
that in a multi-background stack, the 5th card of a bkgnd may be the
20th card of the stack. When on the 20th card, this returns 5.

Examining all the cards in a repeat loop is very slow, especially if we 
are on the last card of a very large background. So instead we do a
binary search, "halving" the remaining cards each time we examine a
card and fail. We always examine the "middle" card of the remaining
cards. After just a few failed attempts, we will match the current
card.

function getBgCardNum
  -- what card number are we searching for?
  put the number of this card into thisCard
  -- We start our search at the middle card of the bg
  put (number of cds of this bg div 2) into nthBgCard
  -- adjust that if this bg has just 1 cd (we can't examine card zero)
  if nthBgCard = 0 then put 1 into nthBgCard
  -- set our "halving" variable, used later to halve the cards
  put nthBgCard into halfTheCards
  repeat
    -- runs until the card number of the "middle" bg card
    -- matches thisCard
    put the number of card nthBgCard of this bg into cardNum
    if cardNum = thisCard then
      return nthBgCard
    else -- not a match
      -- adjust the "middle" card of the remaining cards up or down
      -- by the current size of the remaining cards
      put halfTheCards div 2 into halfTheCards
      if halfTheCards = 0 then put 1 into halfTheCards
      if cardNum > thisCard then
        subtract halfTheCards from nthBgCard
      else
        add halfTheCards to nthBgCard
      end if
    end if
  end repeat
end getBgCardNum

Karl Petersen



Creating ordinals

Posted: 7/2/99
I've found it useful to be able to express numbers as ordinals when
dealing with dates especially. This simple function takes a number,
such as 12, 22, or 3, and returns it with the right ordinal suffix --
say, 12th, 22nd, or 3rd.

function ordinals theNr
  put "st,nd,rd" into tags
  put the length of theNr into y
  repeat with z = 1 to 3
    if (char y of theNr = z) and (char y - 1 of theNr <> "1") then
      put item z of tags after theNr
      exit repeat
    end if
  end repeat
  if theNr is a number then put "th" after theNr
  return theNr
end ordinals

Malcolm Bowers



International Offset Function

Posted: 12/30/98
HyperCard's native text-handling routines are not well-suited to the
needs of users outside the United States, thanks to the fact that many
of these routines ignore the diacritical marks (accents grave,
circumflex; cedilla; etc etc) found in a variety of languages other
than English. In addition, many of these routines are not
case-sensitive (i.e. cannot tell lowercase "a" from uppercase "A"), a
quality which is not always desirable even in the 'States.
Fortunately, HyperCard isn't *completely* blind to diacriticals and
case... and those few text-handling routines which do heed diacriticals 
et al, the "greater than" and "less than" string comparison functions
in particular, can be put to excellent use along this line. The handler 
immediately following is a case-sensitive, diacritical-sensitive offset 
function which illustrates how to go about such things in pure
HyperTalk.

function IntlOffset TargetStr,SearchText
  put the length of TargetStr - 1 into StrLength
  put offset (TargetStr,SearchText) into TheOffset
  repeat until theOffset = 0
    put char TheOffset to (TheOffset + StrLength) of SearchText into TempStr
    if not ((TargetStr < TempStr) or (TargetStr > TempStr)) then exit repeat
    put numToChar(8) into char TheOffset of SearchText -- untypeable char
    put offset (TargetStr,SearchText) into TheOffset
  end repeat
  return TheOffset
end IntlOffset

Quentin Long
Graphic Descriptions



Testing multiple flags at once

Posted: 9/30/98
If you test for a number of flags being empty or not empty, e.g.:

  if who is not empty or what is not empty or when is not empty
  then exit handler

it's shorter and possibly quicker to concatenate them:

  if who & what & when is not empty
  then exit handler

Bob Stelloh
Oakhill Associates



Invisible messages

Posted: 9/30/98
This loads or clears the message box without showing it:

on blindMsg txt
  get blindTyping
  set blindTyping to true
  if txt = empty then type numtochar (127) -- delete it
  else type txt -- replace it
  set blindTyping to it
end blindMsg

Call it like this:

blindMsg "secret msg" -- put that into msg w/o showing it
blindMsg -- empty the msg w/o showing it

Bob Stelloh
Oakhill Associates



Preventing card deletion

Posted: 9/29/98
To prevent card(s) from being deleted, use the following at the
appropriate level, e.g., in the card script for one card or in the bg
script for all cards:

on deletecard
  go prev cd
end deletecard

You will in fact get a glimpse of the previous card.

Bob Stelloh
Oakhill Associates

This appears to be a bug in HyperCard. It may not work in future versions. -- jg



Fast-moving buttons

Posted: 9/8/98
When setting the location of something, this format:

  set the loc of cd btn "Moving Button" to 256,171

will run a lot faster than this in repeat scripts:

  set the loc of cd btn "Moving Button" to "256,171"

Steve Howse



Reserved word "Object" in 2.4

Posted: 9/8/98
As of 2.4, the word "object" is a reserved word. There is a case where
using the word "object" doesn't cause a problem:

on test object
  put the id of object
end test

where "object" contains the name of a valid HyperCard object, like
"card button 1". As it was explained to me, because the word "object"
is the last word in the line, the compiler doesn't try to use it as a
reserved word.

This next script, however, will fail to compile and you'll get a "Can't
understand..." error.

on test object
  put the id of object into foo
end test

Paul Foraker
White Feather Software



"Set" can't be nested

Posted: 3/24/98
The "set" command cannot be nested inside any other "set" commands.
The following handler, which calls the "set" command three times to
accomplish a single evaluation, will fail:

on mouseUp
  set the locktext of cd fld 1 to (extractItem(2) is empty)
end mouseUp

function extractItem theItemNum
  put "one°°three°four°five°six" into data
  put the itemDelimiter into oldDelim
  set the itemDelimiter to "°" -- here is the second "set" call
  put item theItemNum of data into theText
  set the itemDelimiter to oldDelim -- here is another
  return theText
end extractItem

The above routine must be written without nesting in order to work:

on mouseUp
  get extractItem(2)
  set the locktext of cd fld 1 to (it is empty)
end mouseUp

Jacqueline Landman Gay
HyperActive Software



Randomly sorting a list

Posted: 12/20/97
To randomly sort a list in a field:

sort lines of cd fld 1 by random(the number of lines of cd fld 1)

George Barker
University of New South Wales

You can also apply this to lists in variables, or to items in a line. -- jg



Finding chunk expressions in a variable

Posted: 12/12/97
The PowerTools SEARCH XFCN does not provide a method for
returning chunk expressions for strings found in variables which are
relative to the entire text being searched. This can be accomplished
by using the FIND function, a scratch background field, and the
foundChunk function. Unlike offset, find allows multiple ways of
isolating strings of characters. Assign the text to the scratch field
and do a field-specific FIND for the string.

Steve Long
Boeing - Knowledge-Based Product Definition



The whichOne function

Posted: 12/4/97
Tony Root, formerly of HyperPro, came up with this idea which handles
the case where you've got many occurrences of two choices and it is
inconvenient (scripts are already large) to use if/else statements.
Here's one example of using the whichOne function:

on mouseUp
  answer "Do you want Red or Blue?" with "Red" or "Blue"
  answer whichOne(it,"Red","Get the red balloon.","We don't have any blue 
balloons.")
end mouseUp

function whichOne var,test,str1,str2
  return item offset(char 1 of (var = test),"tf") of (str1 &","& str2)
end whichOne

It is not necessary, of course, to return a choice of strings. You
could use this function to select one of two different fields, or click 
on one of two different buttons, go to one of two different cards,
etc.

Paul Foraker
White Feather Software



Deleting in repeat loops

Posted: 11/28/97
Repeat loops are a good way to deal with a list of items one at
a time (such as "if item j of theNumbers < 3 then delete item j
of theNumbers").

But the example I just used also illustrates an error that novices
often encounter. If you are removing things from a list as you
process them, you will skip some items. Consider what happens
when j = 2 and item 2 is removed. Item 3 has now become item 2.
The next item to be tested will be the NEW item 3, which
was originally item 4. The original item 3 is never examined...

Fortunately the solution to this is simple. Examine your list
backwards! Instead of "repeat with j = 1 to 10" use
"repeat with j = 10 down to 1". This way items are only deleted
"behind" the repeat, not "ahead" of it.

Ben Lawson



Bypassing the userlevel with "domenu"

Posted: 11/24/97
If you need to access a menu item but the userlevel is set too low,
send the "doMenu" message directly to HyperCard. For example:

   send "doMenu Delete Card" to HyperCard

Sending the doMenu message bypasses any userlevel limit and the
necessary presence of the "Delete Card" menu item.

Brett Sher



Removing blank lines

Posted: 11/24/97
When removing blank lines from a text list, it is customary to check
each line within a repeat loop to see if the line is empty, and if so,
delete it. It is much faster to use "offset" to check for double
returns and delete the second return in each iteration.

function noBlankLines theList -- removes blank lines
  repeat
    get offset(return & return,theList)
    if it = 0 then exit repeat
    delete char it+1 of theList
  end repeat
  return theList
end noBlankLines

Jacqueline Landman Gay
HyperActive Software



Fastest way to not

Posted: 11/24/97
This:

   not (there is a stack stackname)

is faster than:

   there is not a stack stackname

Tony Berumen
Imagitec



Fastest way to get a stack path

Posted: 11/24/97
The HyperCard function "the stacks" returns a list of all the currently 
open stacks, one per line. The frontmost stack (i.e., the current stack 
window) is always the top line of the list. Therefore, the fastest way
to retrieve the path to the current stack is:

   get line 1 of the stacks

This gives you a clean path name, without the need to parse out the
word "stack", remove quotes, or anything else.

Jacqueline Landman Gay
HyperActive Software

This great tip was brought to our attention by Elaine Ung
at Apple Computer, Inc. -- jg



Setting a true/false variable

Posted: 11/24/97
If you need to set a variable to true or false based on a certain
condition, you can avoid this more cumbersome structure:

   if the optionKey is down
   then put true into var
   else put false into var

by concatenating the test into one line like this:

   put (the optionKey is down) into var

This technique works with any condition that evaluates to true or false.

Jacqueline Landman Gay
HyperActive Software



Simulating 'case' statements

Posted: 11/24/97
I've seen a number of stacks that include code of this general sort:

on WhateverMsg
   put the random of 4 into Fred
   if Fred = 1 then put "Zelda" into George
   if Fred = 2 then put "Henrietta" into George
   if Fred = 3 then put "Xanthippe" into George
   if Fred = 4 then put "Alice" into George
 end WhateverMsg

This is an attempt to re-create the CASE statement within HyperTalk. It
works, but there is a better way:

on WhateverMsg
   put the random of 4 into Fred
   put item Fred of "Zelda,Henrietta,Xanthippe,Alice" into George
 end WhateverMsg

And if you want to do it in *one* line, here's how:

on WhateverMsg
   put item (the random of 4) of "Zelda,Henrietta,Xanthippe,Alice" into George 
end WhateverMsg

Quentin Long
Graphic Descriptions



Restricting "find" to a single card

Posted: 11/24/97
To restrict searches to a single card in a stack, unmark all cards and
then mark only the card (or cards) to which you want to restrict the
search:

     find "theword" of marked cds

works, as long as the stack is not locked or on read-only media
(you can't mark cards in a locked stack.)

Jacqueline Landman Gay
HyperActive Software



Checking case sensitivity

Posted: 11/24/97
A less obvious, but simpler and faster way to do case-sensitive 
comparisons in HyperTalk:

function CaseSensEquals theString1, theString2
  if theString1 < theString2 then
    return false
  else if theString1 > theString2 then
    return false
  else
    return true
  end if
end CaseSensEquals

Using this function:
  CaseSensEquals("hiroko","Hiroko")
would return false, but:
  CaseSensEquals("Hiroko","Hiroko")
would return true.


This works in HyperTalk because the equals operator is case-insensitive, 
while the greater-than and less-than operators are case-sensitive.

Eric Oesterle



Quick "pad" method for columns

Posted: 11/24/97
When putting data into columns in a scrolling field it is customary to
use a mono-spaced font and "pad" the data with spaces, which are added
using a repeat loop. I have found it quicker to replace the repeat loop
for adding spaces with something like this:

put char 1 to (theWidth-length of theText) of "              " before
theText

where there are enough spaces between the quotes to cover your column
width, for worst cases. This puts in the right number of space
characters in one go.

Malcolm Bowers



Debug rebuild font menu

Posted: 11/24/97
If, during a session, you go to a stack that has a custom font
installed that is not in your system, you can use the command 'debug
rebuild font menu' to allow that font to show up in the Font menu. You
only need to use this if you are depending on having the custom font
show up in the Font menu. Otherwise, the custom font will display just
fine in your fields without rebuilding the menu.

Paul Foraker
White Feather Software



Untypeable itemdelimiter

Posted: 11/24/97
A most excellent item delimiter to use for importing and exporting text 
is numToChar(127), the Delete character. If your data has been
generated from keyboard input, there's no way anyone has "typed" a
delete character into the fields, so this is a very safe delimiter.
Since the character has no width, you can add it to visible fields with 
no visual penalty.

Stu MacKenzie

In larger font sizes, the Delete character is represented by a small
box, but in 12-point or smaller sizes, it has no width. -- jg



Removing leading and trailing spaces

Posted: 11/24/97
This function will strip leading and trailing spaces and returns from a 
string.

function noLeadingOrTrailingSpace x
  return word 1 to (number of words of x) of x
end noLeadingOrTrailingSpace

Brett Sher



Fast stack script editing

Posted: 11/24/97
Put a handler in your Home stack script that lets you easily edit the
scripts of other stacks without having to go to the other stack.

on es
  edit the script of stack " " -- notice there's a space there
end es

Then you can type "es" in the message box and HyperCard will prompt you 
to find the stack " ". Find the stack whose script you want to edit
without leaving the stack you're in.

Credit for this handler goes to Brady Johnson.

Dennis Birch



Go safely to a stack

Posted: 11/24/97
Many times there is a need to switch between stacks to get or save data 
or perform any number of functions. If you simply issue the command

  go stack "World's Greatest Stack II"

and the stack is not there then HyperCard intercedes with the dialog
"Where is..." At this point, the script could continue as planned, or
it could end in an error, depending on whether or not the user selected
the correct stack from the "Where is..." dialog. The good way to handle 
this situation is to make sure the stack exists ("if there is a
stack...") and go there if it does, or prompt the user and manage the
response if it doesn't.

Tony Berumen
Imagitec


Up to top | HyperCard Tips and Tricks