-- DROP DOWN LIST -- -- -- 12 - 13 September 1998: written for the D7 Behaviors Palette by -- James Newton -- 28- 30 October 1998: rewritten with help and guidance from -- Pim van Bochoven to include a selection checkmark and to allow the -- use of markers with duplicate names. -- 13 November 1998: additional Public Method handlers added and -- commented. -- 25 April 2002: isOKToAttach() added, general spring cleaning. -- NOTES FOR DEVELOPERS -- -- -- The handlers of this behavior fall into three types: those that -- determine the contents of the list, those that react to the user's -- mouse clicks, and those that allow Lingo access to the current -- selection. -- -- CREATING THE CONTENTS -- The author has a choice of three types of content: #movie, #marker -- or the current contents of the field. The latter two are easy to -- deal with since the information already exists in the form of -- string. (The labelList is in fact a string, not a list). -- -- Creating a RETURN delimited string of movies is more complex. We -- want to list all movies in the same folder as the current movie, or -- which are in subfolders of this folder. The CreateMovieLists -- handler uses recursion. The getNthFileNameInFolder function is used -- to look at each item in the folder in turn. If it is a movie, it -- is added to the list. If not, it is treated as if it were itself a -- folder. The MovieList handler calls a clone of itself to examine -- the contents of this new folder. Any new movies found are added to -- the growing movies variable. -- Once the content has been established, the dimensions of the list -- can be calculated. Setting the rect of a text member changes the -- size of the onstage sprite. It does not in fact change the rect of -- the member. This will always return the full height of the text. -- This behavior exploits this fact to create the myOpenRect -- property. A RETURN character is then added to the end of the list, -- to ensure that the hilite of the last line of the list runs the -- full width of the field. -- ALLOWING FOR CHECKMARKS -- Actually, a RETURN delimited string is not quite enough. I place 3 -- spaces at the beginning of each line, to make room for a checkmark. -- if the menu is set to "select" (rather than ("execute"), then it -- needs to display a checkmark to remind the user which item was -- selected previously. Most characters that would be used as a -- checkmark, in most fonts, will be about two spaces wide. So I -- place the checkmark in spaces one and two. If you use a -- proportional font, this will make the item jump one space to the -- left. To solve this use a character followed by a space as your -- checkmark: "* ". Rather than add and subtract spaces constantly, I -- chose to create a list with number items, containing the original -- text: myItemsList. This has the additional advantage of storing -- the frame numbers for each marker, if you choose to create a list -- of markers. I am indebted to Pim van Bochoven for showing me how -- to create a list which includes duplicate marker names. -- INTERACTING WITH THE USER -- The major difficulty here is to determine the size and position of -- the open list. Ideally, the list should open so that the current -- selection is under the cursor. If the list is too near the top or -- bottom of the Stage for this to happen, its position is shifted up -- or down the appropriate number of lines. -- -- This is not the standard behavior of a pop-up menu. Standard menu -- behavior would require either an Xtra or more than one sprite. -- -- The difference between this list behavior and a pop-up menu becomes -- even clearer if the list is too long to fit on the Stage. If this -- happens, a scrollbar is provided. -- -- A mouseUp can have two meanings: a fast click will hold the list -- open, a longer click indicates that the user has made a selection -- the value of the ticks when the mouse is first pressed is saved in -- myClickTicks. This value is compared against the ticks as soon as -- the mouse is released. If the difference is less than 30 ticks -- (half a second) the list wil remain open. Otherwise, the behavior -- considers that a selection has been made. -- -- If the list remains open, a click elsewhere should close it. The -- prepareFrame handler checks if the clickOn is the number of the -- list sprite. If not, the clicks was elsewhere. -- -- When the list closes, two things must happen: the rect of the -- member has to be set to the height of a single line, and the -- contents of the field must be scrolled so that the selected item -- appears in the remaining space. The scrollTop property gives the -- height in pixels between the start of the text and the top of the -- visible line of text. -- Note the simplicity of the ScrollTo handler. -- ACCESSING THE CURRENT SELECTION -- The DropList_Selection handler gives flexible access to the current -- selection. If only one dropdown list appears in the current frame, -- use.. -- -- put sendAllSprites (#DropList_Selection) -- -- ...to print out a list in the Message Window. If more than one -- list is present, then you can either include the list's name in -- your call: -- -- put sendAllSprites (#DropList_Selection, "Markers") -- -- [#item: 1, #text: "Intro, #type: #marker, #sprite: 1] -- -- ... or send in an empty property list for each dropdown list to -- fill in with its own details: -- -- put sendAllSprites (#DropList_Selection, [:]) -- -- ["Markers": [#item: 1, #text: "Intro", #type: #marker, -- #sprite: 1], "Movies": [#item: 5, #text: "Main", #type: #movie, -- #sprite: 2]] -- -- You could also target the list's sprite directly, with a sendSprite -- call. -- Any of these calls will return a list specifying: -- * the number of the selected item -- * the text of the selected item -- * the type of data concerned (#movie | #marker | #content) -- * the dropdown list's spriteNum -- -- If you collect data for all dropdown lists in a property list, then -- the name of each dropdown list will also be returned (as the -- property for each entry). -- EXECUTING CUSTOM LINGO -- In the Behavior Parameters dialog you can set the list to use the -- "Current contents of the field" and to "Execute... do -- selectedLine". The behavior will now treat each item in the -- dropdown list as a Lingo command. You will need to provide -- handlers in a movie script to treat these commands. -- -- For example, your dropdown list could contain the following items: -- -- Register -- Help -- Quit -- -- Only "Quit" is a Lingo keyword that could be executed directly. -- For the other two items you would need a movie script with the -- following handlers: -- -- on Register -- go marker ("Register") -- end -- -- on Help -- open window "Help" -- end -- ALTERING THE CONTENTS OF THE DROPDOWN LIST -- You can use a #DropList_SetContents call to change the contents of -- the dropdown list on the fly. -- If you send either a RETURN delimited string or a list as a -- parameter, the dropdown list will now behave as if you had chosen -- "Current contents of the field" in the Behavior Parameters dialog. -- The default item will be the first item in your list. Example: -- -- sendSprite (1, #DropList_SetContents, ["Register","Help", "Quit"]) -- -- If the dropdown list is set to "Execute... do selectedLine", then -- make sure that there are handlers in a movie script to deal with -- each command. -- -- You can also use the symbols #marker and #movie as a parameter. -- This will automatically create a list of either markers in the -- current movie, or movies in the current folder and subfolders. -- Examples: -- -- sendSprite (1, #DropList_SetContents, #marker) -- sendAllSprites (#DropList_SetContents, #movie, "List 1") -- CHOOSING TO SELECT OR TO EXECUTE AN ITEM -- You can toggle between the selection and execution modes using a -- #DropList_ToggleExecution call. This takes one or two parameters: -- the new mode and/or the name of the list to toggle. If you use -- both parameters, the new mode should appear first. Example: -- -- sendAllSprites (#DropList_ToggleExecution, #execute, "Movies") -- -- If you do not specify a mode, the mode will switch between #execute -- and #select. If you do not specify a list, then all dropdown lists -- in the current frame will be affected. -- PROPERTIES -- property spriteNum property mySprite -- author-defined parameters property myName property myContent property myAction property myCheckmark property myStandard -- internal properties property myListMember property myField property myItemsList property myRestoreString property myDisplayString -- dimensions property myItemHeight property myOpenRect property myClosedRect property myClosedLoc property myOpenHeight property myStageHeight property myStageWidth -- selection property mySelectedItem property myListIsOpen property myClickTicks property myLastHilite -- EVENT HANDLERS -- on beginSprite me Initialize me end beginSprite on mouseDown me if not myListIsOpen then OpenList me end mouseDown on prepareFrame me CheckListState me end prepareFrame on mouseUp me CheckClick me end mouseUp on mouseUpOutside me CloseList me end mouseUpOutside -- CUSTOM HANDLERS: UTILIZATION -- on CheckListState me if myListIsOpen then if the clickOn <> spriteNum then -- Click outside list while it is held open CloseList me else HiliteSelection me end if else if myContent = #marker and myAction then markerNumber = GetCurrentMarker (me) if mySelectedItem = markerNumber then exit mySelectedItem = markerNumber ScrollTo me, mySelectedItem end if end if end CheckListState on OpenList me -- sent by mouseDown mySprite.locZ = the maxInteger myClickTicks = the ticks myListIsOpen = TRUE myDisplayString = myRestoreString if not myAction then put myCheckMark into myDisplayString.line[mySelectedItem].char [1..2] end if myListMember.text = myDisplayString currentScroll = myListMember.scrollTop if myOpenHeight <= myStageHeight then -- Show whole list to best advantage overShoot = currentScroll - myClosedLoc[2] if overShoot < 0 then overShoot = myOpenHeight - overShoot - myStageHeight if overShoot < 0 then -- Simplest case mySprite.locV = myClosedLoc[2] - currentScroll else -- List too low: shift it up so it will appear in full lineAdjust = ((overShoot - 1) / myItemHeight) + 1 pixelAdjust = (lineAdjust * myItemHeight) - overShoot openTop = myStageHeight - myOpenHeight - pixeladjust mySprite.locV = openTop end if else -- List too high: shift it down so it will appear in full lineAdjust = ((overShoot - 1) / myItemHeight) + 1 pixelAdjust = lineAdjust * myItemHeight openTop = pixelAdjust - overShoot mySprite.locV = openTop end if myListMember.scrollTop = 0 myListMember.rect = myOpenRect else -- List is too long to show in full: show as much as possible... mySprite.locV = -2 clippedRect = myOpenRect.duplicate() clippedRect[4] = myStageHeight myListMember.rect = clippedRect --...and let it a scroll myListMember.boxType = #scroll -- <20000111> -- if mySprite.right > myStageWidth then -- spriteWidth = mySprite.right - mySprite.left -- mySprite.locH = myStageWidth - spriteWidth -- end if -- end if -- <20000111> margins = (myListMember.margin + myListMember.border) * 2 margins = margins + myListMember.boxDropShadow if mySprite.left + myOpenRect.right + margins > myStageWidth then mySprite.locH = myStageWidth - myOpenRect.right - margins scrollAdjust = myClosedLoc[2] - mySprite.locV myListMember.scrolltop = currentScroll - scrollAdjust end if -- updateStage end OpenList on HiliteSelection me -- sent by prepareFrame if the mouseMember <> myListMember then if myLastHilite then -- Clear hilite to indicate that no action will be taken myLastHilite = 0 hilite char the maxInteger of field myField end if exit else if myListMember.boxtype = #scroll then AutoScroll me -- Hilite the line under the mouse listLocV = mouseV() - mySprite.locV + myListMember.scrollTop mouseItem = (listLocV / myItemHeight) + 1 end if if mouseItem = myLastHilite then exit if mouseItem > myItemsList.count then myLastHilite = 0 hilite char the maxInteger of field myField exit end if myLastHilite = mouseItem if mouseItem = 1 then firstCharToHilite = 1 else textBeforeMouseItem = line 1 to (mouseItem - 1) of myDisplayString firstCharToHilite = the number of chars of textBeforeMouseItem + 2 -- Two extra characters = invisible RETURN + first char of -- mouseItem end if mouseItemLength = the number of chars of line mouseItem of myDisplayString lastCharToHilite = firstCharToHilite + mouseItemLength hilite char firstCharToHilite to lastCharToHilite of field myField end HiliteSelection on AutoScroll me -- sent by HiliteSelection scrollDownHeight = myItemHeight / 2 scrollUpHeight = myStageHeight - myItemHeight / 2 currentScroll = myListMember.scrollTop if mouseV() < scrollDownHeight then if currentScroll <> 0 then newScroll = currentScroll - scrollDownHeight myListMember.scrollTop = max (0, newScroll) end if else if mouseV() > scrollUpHeight then maxScroll = myOpenHeight - myStageHeight if currentScroll <> maxScroll then newScroll = currentScroll + scrollDownHeight myListMember.scrollTop = min (maxScroll, newScroll) end if end if end AutoScroll -- <20000726> -- on CheckClick me -- sent by mouseUp if the ticks - myClickTicks < 30 then -- Fast click: hold list open and get ready to react to the -- next click myClickTicks = 0 else if myLastHilite > 0 then -- FIX -- Treat selection and close mySelectedItem = myLastHilite else -- FIX myLastHilite = 0 -- FIX end if CloseList me if myAction and myLastHilite then Execute me end if end if myLastHilite = 0 end CheckClick -- -- on ScrollTo me, theLine -- sent by Initialize, CloseList myListMember.scrollTop = -10 -- D7 won't set the scrollTop if it thinks it hasn't changed myListMember.scrollTop = myItemHeight * (theLine - 1) end ScrollTo on CloseList me -- sent by mouseUpOutside, HiliteSelection, CheckClick mySprite.locZ = spriteNum hilite char the maxInteger of field myField if not myAction then myListMember.text = myRestoreString end if mySprite.loc = myClosedLoc myListMember.boxType = #fixed myListMember.rect = myClosedRect ScrollTo (me, mySelectedItem) myListIsOpen = FALSE if myContent <> #marker then updateStage end CloseList on Execute me -- sent by CheckClick theItem = myItemsList[mySelectedItem] case myContent of #movie: if not (the movieName starts (theItem&".")) then go movie theItem end if #marker: go myItemsList.getPropAt(mySelectedItem) #content: do theItem end case end Execute -- CUSTOM HANDLERS: INITIALIZATION -- on Initialize me -- sent by beginSprite mySprite = sprite(me.spriteNum) myListMember = mySprite.member -- Error checking memberType = myListMember.type if memberType <> #field then ErrorAlert (me, #invalidMemberType, memberType) end if -- End of error checking myField = myListMember.number -- Convert to symbols case myContent of "Current contents of the field": myContent = #content "Markers in this movie": myContent = #marker "Movies with the same path name": myContent = #movie end case -- Convert to boolean case myAction of "Select: return the selected item when called": myAction = FALSE "Execute: go movie | go marker | do selectedLine": myAction = TRUE end case -- Ensure that the field properties are properly set myListMember.wordWrap = FALSE myListMember.alignment = "left" myListMember.boxType = #fixed if myStandard then myListMember.border = 1 myListMember.margin = 2 myListMember.boxDropShadow = 2 end if -- Determine the field's content CreateItems me mySelectedItem = DefaultItem (me) SetDimensions me -- Display closed list myListMember.rect = myClosedRect ScrollTo (me, mySelectedItem) end Initialize on CreateItems me -- sent by Initialize case myContent of #content: CreateContentsLists (me) #marker: myRestoreString = AddSpaces (me, the labelList) myItemsList = GetMarkedFrames (me) #movie: myRestoreString = "" myItemsList = [:] saveDelimiter = the itemDelimiter the itemDelimiter = "." CreateMovieLists (me, the moviePath) set the itemDelimiter = saveDelimiter end case end CreateItems on AddSpaces me, theText -- Adds three spaces to the beginning of every line repeat while the last char of theText = RETURN delete the last char of theText end repeat newString = EMPTY lineCount = theText.line.count repeat while lineCount theItem = theText.line[lineCount] put " "&theItem&RETURN before newString lineCount = lineCount - 1 end repeat return newString end AddSpaces on CreateContentsLists me theText = myListMember.text repeat while the last char of theText = RETURN delete the last char of theText end repeat myRestoreString = EMPTY myItemsList = [:] lineCount = theText.line.count repeat with i = 1 to lineCount theItem = theText.line[i] -- Strip myCheckmark and any spaces if SPACE&myCheckmark contains theItem.char[1] then delete theItem.char[1] repeat while theItem.char[1] = " " delete theItem.char[1] end repeat end if -- Ensure that the line starts with 3 spaces myRestoreString = myRestoreString&" "&theItem&RETURN myItemsList.addProp(i, theItem) end repeat end CreateContentsLists on GetMarkedFrames me markerList = [:] sort markerList lastCheckedMarker = 0 if marker (1) <> marker (-the maxInteger / 2) then -- We're after the first marker repeat with i = 0 down to -the maxInteger checkMarker = marker(i) if checkMarker = lastCheckedMarker then exit repeat lastCheckedMarker = checkMarker markerList.addProp (checkMarker, 0) end repeat end if if marker (0) <> marker (the maxInteger / 2) then -- We're before the last marker repeat with i = 1 to the maxInteger checkMarker = marker(i) if checkMarker = lastCheckedMarker then exit repeat lastCheckedMarker = checkMarker markerList.addProp(checkMarker, 0) end repeat end if i = markerList.count() theLabels = the labelList repeat while i markerList [i] = theLabels.line [i] i = i - 1 end repeat return markerList end GetMarkedFrames on CreateMovieLists me, folderName -- sent by GetListItems -- Recursive handler if (the machineType = 256) then fileDelimiter = "\" else fileDelimiter = ":" end if fileCount = 0 repeat while TRUE fileCount = fileCount + 1 theFileName = getNthFileNameInFolder (folderName, fileCount) if theFileName = EMPTY then -- No more files return -- movieString else -- Check if theFile is a movie case item 2 of theFileName of "dir", "dxr", "dcr": theMovie = item 1 of theFileName myRestoreString = myRestoreString&" "&theMovie&RETURN movieCount = myItemsList.count() + 1 myItemsList.addProp(movieCount, theMovie) otherwise -- Check if theFile is not in fact a folder: use recursion CreateMovieLists (me, folderName&theFileName&fileDelimiter) end case end if end repeat end MovieList on DefaultItem me -- sent by Initialize case myContent of #content: return 1 #marker: return GetCurrentMarker (me) #movie: saveDelimiter = the itemDelimiter set the itemDelimiter to "." shortName = item 1 of the movieName set the itemDelimiter = saveDelimiter return myItemsList.getPos (shortName) end case end DefaultItem on SetDimensions me -- Determine the dimensions of the list -- <20000111> currentWidth = myListMember.width maxWidth = getMaxWidth(me, myRestoreString) -- saveLastChar = the last char of myRestoreString -- Should be RETURN delete the last char of myRestoreString myListMember.text = myRestoreString myItemHeight = myListMember.lineHeight myOpenRect = myListMember.rect myClosedRect = myOpenRect.duplicate() -- <20000111> myOpenRect.right = maxWidth myClosedRect.right= currentWidth -- myClosedRect[4] = myItemHeight + (myListMember.margin / 2) myClosedLoc = mySprite.loc addedHeight = (myListMember.margin * 2) + myListMember.boxDropShadow myOpenHeight = myOpenRect.bottom + addedHeight windowRect = (the activeWindow).rect myStageHeight = windowRect.bottom - windowRect.top myStageWidth = windowRect.right - windowRect.left myRestoreString = myRestoreString&saveLastChar myListMember.text = myRestoreString end SetDimensions -- <20000111> on getMaxWidth(me, theText) myListMember.text = theText myListMember.rect = rect (0, 0, 8000, 0) maxWidth = 0 charCount = theText.char.count lineCount = theText.line.count - 1 -- Extra RETURN at the end repeat while lineCount lineWidth = charPosToLoc(myListMember, charCount).locH if lineWidth > maxWidth then maxWidth = lineWidth end if charsInLine = theText.line[lineCount].char.count + 1 -- ""&RETURN charCount = charCount - charsInLine lineCount = lineCount - 1 end repeat return maxWidth + 4 -- Allows for > before longest line end getMaxWidth -- on GetCurrentMarker me -- sent by Initialize -- Also used to update display if playback head moved by some other means markerPosition = myItemsList.findPos(the frame) if not markerPosition then markerPosition = myItemsList.findPosNear(the frame) - 1 end if return max (1, markerPosition) end GetCurrentMarker -- PUBLIC METHOD (response to #sendSprite, #sendAllSprites, #call) -- on DropList_Selection me, propListOrString -- Returns the current selection of the dropdown list. If you have -- several dropdown lists with different names, then you can use -- sendAllSprites call with a property list as a parameter. -- Example: -- -- put sendAllSprites (#DropList_Selection, [:]) -- -- ["Markers": [#item: 1, #text: "Intro", #type: #marker, -- #sprite: 1], "Movies": [#item: 5, #text: "Main", #type: #movie, -- #sprite: 2]] -- If you want the current selection of a particular list then use -- its name: -- -- put sendAllSprites (#DropList_Selection, "Markers") -- -- [#item: 1, #text: "Intro", #type: #marker, #sprite: 1] -- -- If you know the sprite number of the list, then you can call it -- directly: -- -- put sendSprite (2, #DropList_Selection) -- -- [#item: 5, #text: "Main", #type: #movie, #sprite: 2] if stringP (propListOrString) then if propListOrString <> myName then exit end if data = \ [\ #item: mySelectedItem, \ #text: myItemsList[mySelectedItem], \ #type: myContent, \ #sprite: spriteNum \ ] if ilk (propListOrString) <> #propList then return data else propListOrString.addProp(myName, data) return propListOrString end if end DropList_Selection on DropList_SetContents me, theContents, theListName -- Changes the contents of the dropdown list to theContents. This -- can be either a RETURN delimited string, a list, #marker or -- #movie. "theListName" is an optional parameter which allows you -- to change the contents of a given list by using its name rather -- than by calling a specific sprite number or behavior. Examples: -- -- sendSprite (1, #DropList_SetContents, #movies) -- -- sendAllSprites (#DropList_SetContents, #marker, "List 1") -- -- objectRef = sendAllSprites (#DropList_GetReference, "Lingo") -- call(#DropList_SetContents,objectRef,["Register","Help", "Quit"]) if not voidP (theListName) then if theListName <> myName then exit end if case ilk (theContents) of #string: myListMember.text = theContents myContent = #content Initialize me #list: listItems = "" lineCount = theContents.count repeat with i = 1 to lineCount theLine = string (theContents[i]) put theLine&RETURN after listItems end repeat myListMember.text = listItems myContent = #content Initialize me #symbol: case theContents of #marker: myContent = #marker Initialize me #movie: myContent = #movie Initialize me otherwise return #invalidListContents end case otherwise return #invalidListContents end case end DropList_SetContents -- <20000526> on DropList_SetCurrentItem(me, newItem) case ilk(newItem) of #integer: if newItem > 0 then if newItem <= myItemsList.count() then -- Choose the nth item in myItemsList mySelectedItem = newItem ScrollTo (me, mySelectedItem) return 0 -- no error end if end if #string: i = myItemsList.count() repeat while i if myItemsList[i] = newItem then mySelectedItem = i ScrollTo (me, mySelectedItem) return 0 -- no error end if i = i - 1 end repeat end case return #invalidItem end DropList_SetCurrentItem -- on DropList_ToggleExecution me, executeMode, listName -- Determines whether the dropdown list executes any lingo when an -- item is selected, or whether it simply retains the selection -- data. -- -- "listName" is an optional parameter. You can use it to change -- the setting of a given list by using its name rather than by -- calling a specific sprite number or behavior. This example makes -- the dropdown list named "Movies" execute when an item is -- selected: -- -- sendAllSprites (#DropList_ToggleExecution, TRUE, "Movies") -- -- If you use a sendAllSprites message without the listName -- parameter, all dropdown lists will be affected. This example -- switches all dropdown lists to #select mode: -- -- sendAllSprites (#DropList_ToggleExecution, #select) -- -- "executeMode" can take any of five values: -- * #execute and TRUE will make the dropdown list execute as -- appropriate when an item is selected. -- * #select and FALSE will make the dropdown list do nothing when -- an item is selected. Use a #DropList_Selection call to get the -- selection info. -- * void (or leaving the parameter blank) will toggle the behavior -- between #execute and #select mode. You can use a list name as -- the only parameter; this will toggle the execute mode of the -- named list. This example toggles the execute mode of a -- dropdown list named "Lingo": -- -- sendAllSprites (#DropList_ToggleExecution, "Lingo") -- -- This example sets the dropdown list in sprite 1 to select mode: -- -- sendSprite (1, #DropList_ToggleExecution, FALSE) if not voidP (listName) then if listName <> myName then exit end if else if stringP (executeMode) then -- Treat executeMode as if it were listName and executeMode were -- void if executeMode = myName then myAction = not myAction else exit end if end if if voidP (executeMode) then myAction = not myAction else case executeMode of #execute, TRUE: myAction = TRUE #select, FALSE: myAction = FALSE otherwise return #invalidExecuteMode end case end if end DropList_ToggleExecution on DropList_GetReference me, propListOrString -- Returns the object reference of this behavior. If you have -- several dropdown lists with different names, then you can use -- sendAllSprites call with a property list as a parameter. -- Example: -- -- put sendAllSprites (#DropList_GetReference, [:]) -- -- ["Markers": , "Movies": , "Lingo": ] -- -- If you want the behavior reference of a particular list then use -- its name: -- -- put sendAllSprites (#DropList_GetReference, "Markers") -- -- -- -- If you know the sprite number of the list, then you can call it -- directly: -- -- put sendSprite (1, #DropList_GetReference) -- -- case ilk(propListOrString) of #propList: propListOrString.addProp(myName, me) return propListOrString #string: if propListOrString = myName then return me otherwise return me end case end DropList_GetReference -- ERROR CHECKING -- on ErrorAlert me, theError, data -- sent by getPropertyDescriptionList, Initialize case theError of #getPDLError: alert \ "Error: This behavior works only with Field members."&RETURN&RETURN&\ "Hit OK and then delete this behavior from the sprite."&\ RETURN&RETURN&\ "For more information on deleting Behaviors, see the Help system." if the optionDown then return \ [ \ #getPDLError: \ [ \ #comment: \ "ERROR: Wrong member type. Click 'Cancel'."&RETURN&\ " Use only with Field members.", \ #format: #string, \ #range: [""], \ #default: "" \ ] \ ] end if #invalidMemberType: -- Determine the behavior's name behaviorName = string (me) delete word 1 of behaviorName delete the last word of behaviorName delete the last word of behaviorName alert \ "BEHAVIOR ERROR: Frame "&the frame&", Sprite "&me.spriteNum&RETURN&RETURN&\ "Behavior "&behaviorName&" only works with Field members."&\ RETURN&RETURN&"Current member type = #"&data halt end case end ErrorAlert on PermittedMemberTypes me -- sent by getBehaviorDescription, getPropertyDescriptionList, -- Initialize, ErrorAlert return [#field] end PermittedMemberTypes -- AUTHOR-DEFINED PARAMETERS -- on isOKToAttach(me, aSpriteType, aSpriteNumber) if aSpriteType = #graphic then return sprite(aSpriteNumber).member.type = #field end if return FALSE end isOKToAttach on getPropertyDescriptionList(me) tPropertyList = [:] tPropertyList[ \ #myName] = [ \ #comment: "Name of this list:", \ #format: #string, \ #default: "List "&the currentSpriteNum \ ] tPropertyList[ \ #myContent] = [ \ #comment: "Contents of list:", \ #format: #string, \ #range: \ [\ "Current contents of the field", \ "Markers in this movie", \ "Movies with the same path name" \ ], \ #default: "Current contents of the field" \ ] tPropertyList[ \ #myAction] = [ \ #comment: "Purpose of list:", \ #format: #string, \ #range : \ [ \ "Select: return the selected item when called", \ "Execute: go movie | go marker | do selectedLine" \ ], \ #default: "Select: return the selected item when called" \ ] tPropertyList[ \ #myCheckmark] = [ \ #comment: "Checkmark to indicate currently selected item:", \ #format: #string, \ #default: ">" \ ] tPropertyList[ \ #myStandard] = [ \ #comment: "Use standard style?", \ #format: #boolean, \ #default: TRUE \ ] return tPropertyList end getPropertyDescriptionList on getBehaviorDescription me return \ "DROPDOWN LIST"&RETURN&RETURN&\ "Drop this behavior on a Field member to create a pop-up list. Animations continue while the the list is kept open."&RETURN&RETURN&\ "When the user clicks on the sprite, the dropdown list opens to reveal all its items. if the user immediately releases the mouse, the menu remains open until the next click. When the use selects a menu item, the menu closes up to displays the selected item. If the user clicks elsewhere, the menu closes to display the previously selected item."&RETURN&RETURN&\ "You can use one of two modes for the Dropdown list:"&RETURN&\ "1) To allow the user to make a selection"&RETURN&\ "2) To execute a simple command."&RETURN&RETURN&\ "SELECTION"&RETURN&\ "In the first case, you will need to determine what the user selected. To interrogate the Dropdown list, use syntax similar to the following:"&RETURN&RETURN&\ " put sendAllSprites (#DropList_Selection, 'listName')"&RETURN&RETURN&\ "This returns a property list with all the necessary information:"&\ RETURN&RETURN&\ "-- [#item: 1, #text: 'First choice', #type: #content, #sprite: 1]"&\ RETURN&RETURN&\ "See the 'Notes for developers' in the script itself for more details."&RETURN&RETURN&\ "You can choose any character to act as a checkmark to indicate the previous selection when the dropdown list is open. Depending on the font you use, you may wish to use a checkmark followed by a space. Reopen the Behavior Parameters dialog to make such a change."&RETURN&RETURN&\ "EXECUTION"&RETURN&\ "You can choose to execute three types of command:"&RETURN&\ "a) go marker ()"&RETURN&\ "b) go movie ''"&RETURN&\ "c) do ''"&RETURN&RETURN&\ "The type of command depends on the contents of the list. The behavior can automatically create a list of markers in the current movie, or movies in the current folder... or it can leave the contents of the Field as they are. In this last case, choosing 'Execute' makes the behavior treat the selected item as a Lingo command. You should include handlers in a movie script to deal with such commands."&RETURN&RETURN&\ "TIP: Place the dropdown list sprite in a high channel where it will not be covered by any other sprites."&RETURN&RETURN&\ "PERMITTED MEMBER TYPES:"&RETURN&"Field members"&RETURN&RETURN&\ "PARAMETERS:"&RETURN&\ "* Name of the list (used in sendAllSprite calls)"&RETURN&\ "* Purpose - Choose between:"&RETURN&\ " - Marker: creates a list of markers in current movie"&RETURN&\ " - Movie: creates a list of movies with the same pathName"&RETURN&\ " - Field contents: uses the current contents of the field"&RETURN&\ "* Action on selection - Choose between:"&RETURN&\ " - Execute: go movie | go marker | do selectedLine"&RETURN&\ " - Select: return the selected item if called to do so"&RETURN&\ "* Checkmark to indicate currently selected item"&RETURN&\ "* Standard style: deselect this option if you want to give the Field member a particular border, margin or shadow."&RETURN&RETURN&\ " PUBLIC METHODS:"&RETURN&\ "=> Get info on currently selected item"&RETURN&\ "=> Set the contents of the dropdown list"&RETURN&\ "=> Toggle between Execute and Select modes"&RETURN&\ "=> Get behavior reference" end getBehaviorDescription on getBehaviorTooltip me return \ "Use with Field members only."&RETURN&RETURN&\ "Turn a Field into a pop-up list to execute commands"&RETURN&\ "or store selected data. See the Behavior Description"&RETURN&\ "for tips on executing items with the 'do' command or"&RETURN&\ "accessing the currently selected item using Lingo."&RETURN&RETURN&\ "Options: create a list of movies with the same path"&RETURN&\ "name, or a list of markers in the current movie." end getBehaviorTooltip