[comp.sys.mac.hypercard] Question about protocol for trapping menu items.

ralph@world.std.com (Ralph Lombreglia) (01/24/91)

As the "Palette Maker" card of the "Power Tools" stack advises, I've
trapped the "Compact Stack" menu item in order to close my palettes
before compacting.  However, I can't seem to figure out how to make
the handler restore the palettes after the compaction.  I'm sure
there's a simple answer, but I'm stumped.  Why does the following
*not* work?

on doMenu what
  if what is "Compact Stack" then
    close window "MyPalette"
    pass doMenu
    palette "MyPalette", "345,80"
  else pass doMenu
end doMenu

Thanks.
Ralph Lombreglia
Internet: ralph@world.std.com   MCI Mail: rlombreglia   Bix: rlombreglia

jdevoto@Apple.COM (Jeanne A. E. DeVoto) (01/24/91)

In article <1991Jan23.234729.7744@world.std.com> ralph@world.std.com
(Ralph Lombreglia) writes:
>Why does the following *not* work?
>
>on doMenu what
>  if what is "Compact Stack" then
>    close window "MyPalette"
>    pass doMenu
>    palette "MyPalette", "345,80"
>  else pass doMenu
>end doMenu

The reason this doesn't work is that the "pass" keyword halts the current
handler. "Pass" means "forget this handler and just pass the message on
to the next level of the message hierarchy."

This should work:

  on doMenu theItem
    if theItem is "Compact Stack" then
      close window "MyPalette"
      send "doMenu Compact Stack" to HyperCard -- avoids recursion
      palette "MyPalette", "345,80"
    else pass doMenu
  end doMenu
-- 
========= jeanne a. e. devoto ========================================
 jdevoto@apple.com     |  You may not distribute this article under a
 jdevoto@well.sf.ca.us |  compilation copyright without my permission.
______________________________________________________________________
 Apple Computer and I are not authorized      |        CI$: 72411,165
 to speak for each other.                     |

jk3t+@andrew.cmu.edu (Jonathan King) (01/24/91)

ralph@world.std.com (Ralph Lombreglia) writes:
> However, I can't seem to figure out how to make
> the handler restore the palettes after the compaction.  I'm sure
> there's a simple answer, but I'm stumped.  Why does the following
> *not* work?
> 
> on doMenu what
>   if what is "Compact Stack" then
>     close window "MyPalette"
>     pass doMenu
      ^^^^ ^^^^^^
>     palette "MyPalette", "345,80"
>   else pass doMenu
> end doMenu

Your problem is this:  the keyword "pass", in addition to passing a
message along the message path *immediately stops execution of the
handler*.  So, in this case, your palette command never executes.
Note that the obvious workaround, replacing "pass domenu" with "domenu
Compact Stack" could make your handler trip over itself.  Using 'send
"domenu Compact Stack" to Hypercard' will probably work, but it 
facistically bypasses any other handlers on the way that want to 
prepare for stack compaction.  About the best you can do here, it
would seem, is to send the "domenu" one notch higher along the message
path.  So, if this handler were on a card, it would read:

 on doMenu what
   if what is "Compact Stack" then
     close window "MyPalette"
     send "domenu Compact Stack" to this background
     palette "MyPalette", "345,80"
   else pass doMenu
 end doMenu

> Thanks.

Hope this works...

jking

ech@cbnewsk.att.com (ned.horvath) (01/25/91)

In article <1991Jan23.234729.7744@world.std.com> ralph@world.std.com
(Ralph Lombreglia) writes:
>Why does the following *not* work?

>on doMenu what
>  if what is "Compact Stack" then
>    close window "MyPalette"
>    pass doMenu
>    palette "MyPalette", "345,80"
>  else pass doMenu
>end doMenu

From article <48422@apple.Apple.COM>, by jdevoto@Apple.COM (Jeanne A. E. DeVoto):
> The reason this doesn't work is that the "pass" keyword halts the current
> handler. "Pass" means "forget this handler and just pass the message on
> to the next level of the message hierarchy."
> 
> This should work:
> 
>   on doMenu theItem
>     if theItem is "Compact Stack" then
>       close window "MyPalette"
>       send "doMenu Compact Stack" to HyperCard -- avoids recursion
>       palette "MyPalette", "345,80"
>     else pass doMenu
>   end doMenu

This isn't quite satisfactory: in general, you want to give all the rest
of the doMenu handlers in the handler chain a shot at the Compact Stack
message.  But as far as I know, the only way to refer to "the next handler
in the chain" is to pass to it.

Why is this reminiscent of "don't tail patch"?

=Ned Horvath=
ehorvath@attmail.com
has to know

John_Miller@mindlink.UUCP (John Miller) (01/29/91)

> Ok, but what if you're the top stack in the stacksInUse and
> there's another stack below you that also has a doMenu > handler that closes
its windows?  If you send the command
> straight to HyperCard the other stacks never get it.  I've
> been wrestling with this general problem (passing on a message
> so other stacks get it but still doing something after the
> message is "finished") and I haven't come up with a good solution.
> I've resorted to setting a global flag and checking it in an idle
> handler, but I don't really like doing it that way.  Would it be
> better to find my stack's place in the stacksInUse list and send
> the message on to the following stack?  Any suggestions?

A solution is to use a global variable, but it doesn't have to
be within the idle handler.  It can all be handled within
the doMenu menu handler.  The trick is to deliberately
cause one level of recursion.  This code relies on the
fact that a global variable that is declared but not
initialized is considered to be empty:  a very useful
feature of HyperTalk.

on doMenu theItem
  -- This global is only declared in this handler:  nowhere else
  global amRecursing
  if amRecursing is not empty then
    play flute   -- Remove from production code :)
    pass doMenu
  else
    if theItem is "Compact Stack" then
      close window "MyPalette"
      put true into amRecursing -- Any non-empty value would do
      send "doMenu Compact Stack" to me -- causes recursion
      play boing  -- Remove from production code :)
      put empty into amRecursing
      palette "MyPalette", "345,80"
    else pass doMenu
  end if
end doMenu


Actually, is the warning about hiding palettes valid?
HyperCard doesn't complain when I try to compact
a stack with a custom palette.  It does complain
when I try to compact a stack X that contains a windoid
that is controlled by an XCMD located in stack X.
Presumably, the Palette XCMD "lets go" of the PLTE and
PICT resources once it has created the window. (Yes? No?)

An unrelated palette question:  is the format of the
PLTE resource documented or do I have to reverse-engineer
it.  I was thinking of creating some XCMDs that would allow
customization:  "If you don't like the default look of
the windoid, create your own palette using the Palette
Maker.  Your palette should have four buttons:  the first
will invoke ... the second will invoke ...."

----------------------------------------------------------------------
John Miller                         (604) 433-1795
Symplex Systems                     AppleLink (rarely)  CDA0461
Burnaby, British Columbia           Fax: (604) 430-8516
Canada                              usenet:  john_miller@mindlink.uucp
----------------------------------------------------------------------

taylorj@yvax.byu.edu (01/30/91)

In <48422@apple.Apple.COM>, jdevoto@Apple.COM (Jeanne A. E. DeVoto) writes:

>  on doMenu theItem
>    if theItem is "Compact Stack" then
>      close window "MyPalette"
>      send "doMenu Compact Stack" to HyperCard -- avoids recursion
>      palette "MyPalette", "345,80"
>    else pass doMenu
>  end doMenu

Ok, but what if you're the top stack in the stacksInUse and there's another
stack below you that also has a doMenu handler that closes its windows?  If you
send the command straight to HyperCard the other stacks never get it.  I've
been wrestling with this general problem (passing on a message so other stacks
get it but still doing something after the message is "finished") and I haven't
come up with a good solution.  I've resorted to setting a global flag and
checking it in an idle handler, but I don't really like doing it that way.
Would it be better to find my stack's place in the stacksInUse list and send
the message on to the following stack?  Any suggestions?

Jim Taylor
Microcomputer Support for Curriculum  |
Brigham Young University              |  Bitnet: taylorj@byuvax.bitnet
101 HRCB, Provo, UT  84602            |  Internet: taylorj@yvax.byu.edu

jk3t+@andrew.cmu.edu (Jonathan King) (01/31/91)

taylorj@yvax.byu.edu writes:
> In <48422@apple.Apple.COM>, jdevoto@Apple.COM (Jeanne A. E. DeVoto) writes:
> 
> >  on doMenu theItem
> >    if theItem is "Compact Stack" then
> >      close window "MyPalette"
> >      send "doMenu Compact Stack" to HyperCard -- avoids recursion
> >      palette "MyPalette", "345,80"
> >    else pass doMenu
> >  end doMenu
> 
> Ok, but what if you're the top stack in the stacksInUse and there's 
> another stack below you that also has a doMenu handler that closes 
> its windows?  If you send the command straight to HyperCard the
> other stacks never get it.  

Yup.  I pointed this out, too, in my response to this message.  And
the problem isn't just limited to stacks in use; it could occur
anywhere you send a message to Hypercard.  But it's a particular
problem when possibly several different handlers along the message
path have to handle a message or Bad Things happen.

> I've been wrestling with this general problem (passing on a message 
> so other stacks get it but still doing something after the message 
> is "finished") and I haven't come up with a good solution.  I've 
> resorted to setting a global flag and checking it in an idle handler, 
> but I don't really like doing it that way. Would it be better to
> find my stack's place in the stacksInUse list and send
> the message on to the following stack?  Any suggestions?

I've come to the decision that too many global flag/ idle handler
combos are a Bad Thing, although they can be unavoidable sometimes.  I
once started writing a function nextobject(current object) that would
return the next object in the message passing hierarchy given the name
of the current object, but it got pretty hairy and inelegant.  You had
to chase down the background of a card, (not too hard, but it isn't
included in the long name of the card), test if you were "Home", maybe
sort through the stacksinuse...I just didn't want to do the general
case badly enough.  And anyway, it seemed really obvious that there
should be something like "the next handler" in the language itself to
cover this situation: send "foo" to the next handler.

Any chance of seeing this in a future version of Hypercard?

jking

jdevoto@Apple.COM (Jeanne A. E. DeVoto) (02/03/91)

In article <8bdsGB_00WB4E=Yksu@andrew.cmu.edu> jk3t+@andrew.cmu.edu
(Jonathan King)(>) and taylorj@yvax.byu.edu point out a problem with
my handler:
  on doMenu theItem
    if theItem is "Compact Stack" then
      close window "MyPalette"
      send "doMenu Compact Stack" to HyperCard -- avoids recursion
      palette "MyPalette", "345,80"
    else pass doMenu
  end doMenu
and more generally with handlers that send their parameters to HyperCard
to avoid infinite recursion problems:
 
>> Ok, but what if you're the top stack in the stacksInUse and there's 
>> another stack below you that also has a doMenu handler that closes 
>> its windows?  If you send the command straight to HyperCard the
>> other stacks never get it.  
>
>Yup.  I pointed this out, too, in my response to this message.  And
>the problem isn't just limited to stacks in use; it could occur
>anywhere you send a message to Hypercard.  But it's a particular
>problem when possibly several different handlers along the message
>path have to handle a message or Bad Things happen.

Here is an improvement. The below is not quite a general case solution,
since it won't work in a card script (because doMenu is initially sent
to the current card, "the target" will be the same as "me", resulting
in passing the doMenu without doing the palette manipulations, if the
below handler is in a card's script). However, most handlers of this
type are in a background or stack script in any case.

on doMenu theItem
  if theItem is "Compact Stack" then
    if the target is not the name of me then
      -- first pass
      close window "My Palette"
      send "doMenu Compact Stack" to me
      palette "My Palette","345,80"
    else
      -- second pass
      pass doMenu
    end if
  else pass doMenu
end doMenu

This avoids the use of a global flag to determine whether the handler
has recursed; instead, the message is sent to "me" the first time around,
and on the second pass, the target is therefore the same as "me". The
handler checks for this and simply passes the message in this case. The
message logic looks like this:

  doMenu (sent as system message when user chooses the menu item)
    close
    doMenu (sent to "me" by doMenu handler)
      pass doMenu up the hierarchy, and afterwards
    palette (performed by the original doMenu handler)

-- 
========= jeanne a. e. devoto ========================================
 jdevoto@apple.com     |  You may not distribute this article under a
 jdevoto@well.sf.ca.us |  compilation copyright without my permission.
______________________________________________________________________
 Apple Computer and I are not authorized      |        CI$: 72411,165
 to speak for each other.                     |