<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.secondlife.com/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=SimonT+Quinnell</id>
	<title>Second Life Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.secondlife.com/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=SimonT+Quinnell"/>
	<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/wiki/Special:Contributions/SimonT_Quinnell"/>
	<updated>2026-07-27T02:17:27Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.42.1</generator>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1190076</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1190076"/>
		<updated>2014-04-27T23:21:32Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* The Script */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users, scripts and prims.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;FALSE&amp;quot;;      //Navigation buttons not displayed&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;TopLeft~Top~TopRight~Left~Center~Right~BottomLeft~Bottom~BottomRight&amp;quot;;   // The buttons, each button separated by a &#039;~&#039;.&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|TopLeft|| Top|| TopRight&lt;br /&gt;
|-&lt;br /&gt;
|Left|| Center || Right&lt;br /&gt;
|-&lt;br /&gt;
|BottomLeft|| Bottom || BottomRight&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu with Auto-Navigation Buttons===&lt;br /&gt;
In this case we now add navigation buttons to the bottom of the script.  Back and Exit buttons are added to the bottom and if necessary &amp;gt;&amp;gt; and &amp;lt;&amp;lt; buttons.  In the script that is calling the menu I use a function something like this.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;      //The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like this.&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.  Here is the menu call.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            &lt;br /&gt;
            if (menuDescriptor == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
            {    // Is this a response to &amp;quot;mymenu&amp;quot; from the dialog script?&lt;br /&gt;
                 string  item = llList2String(returnMenu,1);             // The actual button pushed&lt;br /&gt;
&lt;br /&gt;
                 // id == the key of the menu user.&lt;br /&gt;
                 // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             if (message == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
             {&lt;br /&gt;
                // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuidentifier | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
// Return is in the format&lt;br /&gt;
// &amp;quot;menuidentifier | item&amp;quot;&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell&lt;br /&gt;
//&lt;br /&gt;
// CHANGES&lt;br /&gt;
// 2010/10/14 - Timeout message now gets sent to the prim that called the menu, not LINK_THIS.  Also includes menuidentifier&lt;br /&gt;
// 2010/11/29 - Fixed Bug in RemoveUser function.  Thanks for Virtouse Lilienthal for pointing it out.&lt;br /&gt;
// 2010/11/29 - Tidied up a little and removed functions NewMenu and RemoveUser that are only called once&lt;br /&gt;
// 2014/04/28 - Clarified licence&lt;br /&gt;
//&lt;br /&gt;
// NOTE: This script is licenced using the Creative Commons Attribution-Share Alike 3.0 license&lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llSay(DEBUG_CHANNEL,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key     menuId = llList2Key(menusDescription, index+1);&lt;br /&gt;
        string  menuDetails = llList2String(menusDescription, index+2);&lt;br /&gt;
        integer menuLink = llList2Integer(menusDescription, index+3);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION - 1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(menuLink, LINK_MENU_TIMEOUT, menuDetails, menuId);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY)&lt;br /&gt;
        {   // Setup New Menu&lt;br /&gt;
            list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
            if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
            {&lt;br /&gt;
                menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
                DisplayMenu(id, channel, 0);&lt;br /&gt;
            }&lt;br /&gt;
            else llSay (DEBUG_CHANNEL, &amp;quot;ERROR in &amp;quot;+llGetScriptName()+&amp;quot;: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE)&lt;br /&gt;
        {    // Will remove all menus that have the user id.&lt;br /&gt;
             integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
             while (~index_id) &lt;br /&gt;
             {&lt;br /&gt;
                 integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
                 RemoveMenu(channel, FALSE);&lt;br /&gt;
        &lt;br /&gt;
                 // Check for another menu by same user&lt;br /&gt;
                 index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;br /&gt;
[[Category:LSL Library|Dialog Handler (MultiUser)]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1190074</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1190074"/>
		<updated>2014-04-27T23:20:40Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* The Script */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users, scripts and prims.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;FALSE&amp;quot;;      //Navigation buttons not displayed&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;TopLeft~Top~TopRight~Left~Center~Right~BottomLeft~Bottom~BottomRight&amp;quot;;   // The buttons, each button separated by a &#039;~&#039;.&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|TopLeft|| Top|| TopRight&lt;br /&gt;
|-&lt;br /&gt;
|Left|| Center || Right&lt;br /&gt;
|-&lt;br /&gt;
|BottomLeft|| Bottom || BottomRight&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu with Auto-Navigation Buttons===&lt;br /&gt;
In this case we now add navigation buttons to the bottom of the script.  Back and Exit buttons are added to the bottom and if necessary &amp;gt;&amp;gt; and &amp;lt;&amp;lt; buttons.  In the script that is calling the menu I use a function something like this.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;      //The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like this.&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.  Here is the menu call.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            &lt;br /&gt;
            if (menuDescriptor == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
            {    // Is this a response to &amp;quot;mymenu&amp;quot; from the dialog script?&lt;br /&gt;
                 string  item = llList2String(returnMenu,1);             // The actual button pushed&lt;br /&gt;
&lt;br /&gt;
                 // id == the key of the menu user.&lt;br /&gt;
                 // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             if (message == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
             {&lt;br /&gt;
                // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuidentifier | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
// Return is in the format&lt;br /&gt;
// &amp;quot;menuidentifier | item&amp;quot;&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell&lt;br /&gt;
//&lt;br /&gt;
// CHANGES&lt;br /&gt;
// 2010/10/14 - Timeout message now gets sent to the prim that called the menu, not LINK_THIS.  Also includes menuidentifier&lt;br /&gt;
// 2010/11/29 - Fixed Bug in RemoveUser function.  Thanks for Virtouse Lilienthal for pointing it out.&lt;br /&gt;
// 2010/11/29 - Tidied up a little and removed functions NewMenu and RemoveUser that are only called once&lt;br /&gt;
// 2014/04/28 - Clarified licence&lt;br /&gt;
//&lt;br /&gt;
// NOTE: This script is licenced using the Creative Commons Creative Commons Attribution-Share Alike 3.0 license&lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llSay(DEBUG_CHANNEL,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key     menuId = llList2Key(menusDescription, index+1);&lt;br /&gt;
        string  menuDetails = llList2String(menusDescription, index+2);&lt;br /&gt;
        integer menuLink = llList2Integer(menusDescription, index+3);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION - 1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(menuLink, LINK_MENU_TIMEOUT, menuDetails, menuId);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY)&lt;br /&gt;
        {   // Setup New Menu&lt;br /&gt;
            list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
            if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
            {&lt;br /&gt;
                menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
                DisplayMenu(id, channel, 0);&lt;br /&gt;
            }&lt;br /&gt;
            else llSay (DEBUG_CHANNEL, &amp;quot;ERROR in &amp;quot;+llGetScriptName()+&amp;quot;: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE)&lt;br /&gt;
        {    // Will remove all menus that have the user id.&lt;br /&gt;
             integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
             while (~index_id) &lt;br /&gt;
             {&lt;br /&gt;
                 integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
                 RemoveMenu(channel, FALSE);&lt;br /&gt;
        &lt;br /&gt;
                 // Check for another menu by same user&lt;br /&gt;
                 index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;br /&gt;
[[Category:LSL Library|Dialog Handler (MultiUser)]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1190073</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1190073"/>
		<updated>2014-04-27T23:20:06Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* The Script */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users, scripts and prims.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;FALSE&amp;quot;;      //Navigation buttons not displayed&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;TopLeft~Top~TopRight~Left~Center~Right~BottomLeft~Bottom~BottomRight&amp;quot;;   // The buttons, each button separated by a &#039;~&#039;.&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|TopLeft|| Top|| TopRight&lt;br /&gt;
|-&lt;br /&gt;
|Left|| Center || Right&lt;br /&gt;
|-&lt;br /&gt;
|BottomLeft|| Bottom || BottomRight&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu with Auto-Navigation Buttons===&lt;br /&gt;
In this case we now add navigation buttons to the bottom of the script.  Back and Exit buttons are added to the bottom and if necessary &amp;gt;&amp;gt; and &amp;lt;&amp;lt; buttons.  In the script that is calling the menu I use a function something like this.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;      //The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like this.&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.  Here is the menu call.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            &lt;br /&gt;
            if (menuDescriptor == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
            {    // Is this a response to &amp;quot;mymenu&amp;quot; from the dialog script?&lt;br /&gt;
                 string  item = llList2String(returnMenu,1);             // The actual button pushed&lt;br /&gt;
&lt;br /&gt;
                 // id == the key of the menu user.&lt;br /&gt;
                 // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             if (message == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
             {&lt;br /&gt;
                // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuidentifier | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
// Return is in the format&lt;br /&gt;
// &amp;quot;menuidentifier | item&amp;quot;&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell&lt;br /&gt;
//&lt;br /&gt;
// CHANGES&lt;br /&gt;
// 2010/10/14 - Timeout message now gets sent to the prim that called the menu, not LINK_THIS.  Also includes menuidentifier&lt;br /&gt;
// 2010/11/29 - Fixed Bug in RemoveUser function.  Thanks for Virtouse Lilienthal for pointing it out.&lt;br /&gt;
// 2010/11/29 - Tidied up a little and removed functions NewMenu and RemoveUser that are only called once&lt;br /&gt;
// 2014/04/28 - Clarified licence&lt;br /&gt;
//&lt;br /&gt;
// NOTE: This script is licence using the Creative Commons Creative Commons Attribution-Share Alike 3.0 license&lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llSay(DEBUG_CHANNEL,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key     menuId = llList2Key(menusDescription, index+1);&lt;br /&gt;
        string  menuDetails = llList2String(menusDescription, index+2);&lt;br /&gt;
        integer menuLink = llList2Integer(menusDescription, index+3);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION - 1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(menuLink, LINK_MENU_TIMEOUT, menuDetails, menuId);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY)&lt;br /&gt;
        {   // Setup New Menu&lt;br /&gt;
            list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
            if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
            {&lt;br /&gt;
                menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
                DisplayMenu(id, channel, 0);&lt;br /&gt;
            }&lt;br /&gt;
            else llSay (DEBUG_CHANNEL, &amp;quot;ERROR in &amp;quot;+llGetScriptName()+&amp;quot;: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE)&lt;br /&gt;
        {    // Will remove all menus that have the user id.&lt;br /&gt;
             integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
             while (~index_id) &lt;br /&gt;
             {&lt;br /&gt;
                 integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
                 RemoveMenu(channel, FALSE);&lt;br /&gt;
        &lt;br /&gt;
                 // Check for another menu by same user&lt;br /&gt;
                 index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;br /&gt;
[[Category:LSL Library|Dialog Handler (MultiUser)]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1115922</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1115922"/>
		<updated>2010-11-28T23:58:29Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* The Script */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users, scripts and prims.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;FALSE&amp;quot;;      //Navigation buttons not displayed&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;TopLeft~Top~TopRight~Left~Center~Right~BottomLeft~Bottom~BottomRight&amp;quot;;   // The buttons, each button separated by a &#039;~&#039;.&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|TopLeft|| Top|| TopRight&lt;br /&gt;
|-&lt;br /&gt;
|Left|| Center || Right&lt;br /&gt;
|-&lt;br /&gt;
|BottomLeft|| Bottom || BottomRight&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu with Auto-Navigation Buttons===&lt;br /&gt;
In this case we now add navigation buttons to the bottom of the script.  Back and Exit buttons are added to the bottom and if necessary &amp;gt;&amp;gt; and &amp;lt;&amp;lt; buttons.  In the script that is calling the menu I use a function something like this.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;      //The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like this.&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.  Here is the menu call.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            &lt;br /&gt;
            if (menuDescriptor == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
            {    // Is this a response to &amp;quot;mymenu&amp;quot; from the dialog script?&lt;br /&gt;
                 string  item = llList2String(returnMenu,1);             // The actual button pushed&lt;br /&gt;
&lt;br /&gt;
                 // id == the key of the menu user.&lt;br /&gt;
                 // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             if (message == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
             {&lt;br /&gt;
                // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuidentifier | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
// Return is in the format&lt;br /&gt;
// &amp;quot;menuidentifier | item&amp;quot;&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell&lt;br /&gt;
//&lt;br /&gt;
// CHANGES&lt;br /&gt;
// 2010/10/14 - Timeout message now gets sent to the prim that called the menu, not LINK_THIS.  Also includes menuidentifier&lt;br /&gt;
// 2010/11/29 - Fixed Bug in RemoveUser function.  Thanks for Virtouse Lilienthal for pointing it out.&lt;br /&gt;
// 2010/11/29 - Tidied up a little and removed functions NewMenu and RemoveUser that are only called once&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llSay(DEBUG_CHANNEL,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key     menuId = llList2Key(menusDescription, index+1);&lt;br /&gt;
        string  menuDetails = llList2String(menusDescription, index+2);&lt;br /&gt;
        integer menuLink = llList2Integer(menusDescription, index+3);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION - 1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(menuLink, LINK_MENU_TIMEOUT, menuDetails, menuId);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY)&lt;br /&gt;
        {   // Setup New Menu&lt;br /&gt;
            list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
            if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
            {&lt;br /&gt;
                menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
                DisplayMenu(id, channel, 0);&lt;br /&gt;
            }&lt;br /&gt;
            else llSay (DEBUG_CHANNEL, &amp;quot;ERROR in &amp;quot;+llGetScriptName()+&amp;quot;: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE)&lt;br /&gt;
        {    // Will remove all menus that have the user id.&lt;br /&gt;
             integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
             while (~index_id) &lt;br /&gt;
             {&lt;br /&gt;
                 integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
                 RemoveMenu(channel, FALSE);&lt;br /&gt;
        &lt;br /&gt;
                 // Check for another menu by same user&lt;br /&gt;
                 index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
             }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;br /&gt;
[[Category:LSL Library|Dialog Handler (MultiUser)]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1115912</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1115912"/>
		<updated>2010-11-28T23:07:35Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* The Script */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users, scripts and prims.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;FALSE&amp;quot;;      //Navigation buttons not displayed&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;TopLeft~Top~TopRight~Left~Center~Right~BottomLeft~Bottom~BottomRight&amp;quot;;   // The buttons, each button separated by a &#039;~&#039;.&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|TopLeft|| Top|| TopRight&lt;br /&gt;
|-&lt;br /&gt;
|Left|| Center || Right&lt;br /&gt;
|-&lt;br /&gt;
|BottomLeft|| Bottom || BottomRight&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu with Auto-Navigation Buttons===&lt;br /&gt;
In this case we now add navigation buttons to the bottom of the script.  Back and Exit buttons are added to the bottom and if necessary &amp;gt;&amp;gt; and &amp;lt;&amp;lt; buttons.  In the script that is calling the menu I use a function something like this.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;      //The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like this.&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.  Here is the menu call.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            &lt;br /&gt;
            if (menuDescriptor == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
            {    // Is this a response to &amp;quot;mymenu&amp;quot; from the dialog script?&lt;br /&gt;
                 string  item = llList2String(returnMenu,1);             // The actual button pushed&lt;br /&gt;
&lt;br /&gt;
                 // id == the key of the menu user.&lt;br /&gt;
                 // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             if (message == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
             {&lt;br /&gt;
                // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuidentifier | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
// Return is in the format&lt;br /&gt;
// &amp;quot;menuidentifier | item&amp;quot;&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell&lt;br /&gt;
//&lt;br /&gt;
// CHANGES&lt;br /&gt;
// 2010/10/14 - Timeout message now gets sent to the prim that called the menu, not LINK_THIS.  Also includes menuidentifier&lt;br /&gt;
// 2010/11/29 - Fixed Bug in RemoveUser function.  Thanks for Virtouse Lilienthal for pointing it out.&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llSay(DEBUG_CHANNEL,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{   // Will remove all menus that have the user id.&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
        &lt;br /&gt;
        // Check for another menu by same user&lt;br /&gt;
        index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key     menuId = llList2Key(menusDescription, index+1);&lt;br /&gt;
        string  menuDetails = llList2String(menusDescription, index+2);&lt;br /&gt;
        integer menuLink = llList2Integer(menusDescription, index+3);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION - 1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(menuLink, LINK_MENU_TIMEOUT, menuDetails, menuId);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;br /&gt;
[[Category:LSL Library|Dialog Handler (MultiUser)]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1115902</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1115902"/>
		<updated>2010-11-28T23:04:36Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* The Script */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users, scripts and prims.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;FALSE&amp;quot;;      //Navigation buttons not displayed&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;TopLeft~Top~TopRight~Left~Center~Right~BottomLeft~Bottom~BottomRight&amp;quot;;   // The buttons, each button separated by a &#039;~&#039;.&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|TopLeft|| Top|| TopRight&lt;br /&gt;
|-&lt;br /&gt;
|Left|| Center || Right&lt;br /&gt;
|-&lt;br /&gt;
|BottomLeft|| Bottom || BottomRight&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu with Auto-Navigation Buttons===&lt;br /&gt;
In this case we now add navigation buttons to the bottom of the script.  Back and Exit buttons are added to the bottom and if necessary &amp;gt;&amp;gt; and &amp;lt;&amp;lt; buttons.  In the script that is calling the menu I use a function something like this.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;      //The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like this.&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.  Here is the menu call.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            &lt;br /&gt;
            if (menuDescriptor == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
            {    // Is this a response to &amp;quot;mymenu&amp;quot; from the dialog script?&lt;br /&gt;
                 string  item = llList2String(returnMenu,1);             // The actual button pushed&lt;br /&gt;
&lt;br /&gt;
                 // id == the key of the menu user.&lt;br /&gt;
                 // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             if (message == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
             {&lt;br /&gt;
                // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuidentifier | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
// Return is in the format&lt;br /&gt;
// &amp;quot;menuidentifier | item&amp;quot;&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell&lt;br /&gt;
//&lt;br /&gt;
// CHANGES&lt;br /&gt;
// 2010/10/14 - Timeout message now gets sent to the prim that called the menu, not LINK_THIS.  Also includes menuidentifier&lt;br /&gt;
// 2010/11/29 - Fixed Bug in RemoveUser function.  Thanks for Virtouse Lilienthal for pointing it out.&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llSay(DEBUG_CHANNEL,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{   // Will remove all menus that have the user id.&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key     menuId = llList2Key(menusDescription, index+1);&lt;br /&gt;
        string  menuDetails = llList2String(menusDescription, index+2);&lt;br /&gt;
        integer menuLink = llList2Integer(menusDescription, index+3);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION - 1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(menuLink, LINK_MENU_TIMEOUT, menuDetails, menuId);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;br /&gt;
[[Category:LSL Library|Dialog Handler (MultiUser)]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062433</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062433"/>
		<updated>2010-10-14T04:46:12Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users, scripts and prims.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;FALSE&amp;quot;;      //Navigation buttons not displayed&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;TopLeft~Top~TopRight~Left~Center~Right~BottomLeft~Bottom~BottomRight&amp;quot;;   // The buttons, each button separated by a &#039;~&#039;.&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|TopLeft|| Top|| TopRight&lt;br /&gt;
|-&lt;br /&gt;
|Left|| Center || Right&lt;br /&gt;
|-&lt;br /&gt;
|BottomLeft|| Bottom || BottomRight&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu with Auto-Navigation Buttons===&lt;br /&gt;
In this case we now add navigation buttons to the bottom of the script.  Back and Exit buttons are added to the bottom and if necessary &amp;gt;&amp;gt; and &amp;lt;&amp;lt; buttons.  In the script that is calling the menu I use a function something like this.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;      //The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like this.&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.  Here is the menu call.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            &lt;br /&gt;
            if (menuDescriptor == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
            {    // Is this a response to &amp;quot;mymenu&amp;quot; from the dialog script?&lt;br /&gt;
                 string  item = llList2String(returnMenu,1);             // The actual button pushed&lt;br /&gt;
&lt;br /&gt;
                 // id == the key of the menu user.&lt;br /&gt;
                 // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             if (message == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
             {&lt;br /&gt;
                // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuidentifier | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
// Return is in the format&lt;br /&gt;
// &amp;quot;menuidentifier | item&amp;quot;&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell&lt;br /&gt;
//&lt;br /&gt;
// CHANGES&lt;br /&gt;
// 2010/10/14 - Timeout message now gets sent to the prim that called the menu, not LINK_THIS.  Also includes menuidentifier&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llSay(DEBUG_CHANNEL,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key     menuId = llList2Key(menusDescription, index+1);&lt;br /&gt;
        string  menuDetails = llList2String(menusDescription, index+2);&lt;br /&gt;
        integer menuLink = llList2Integer(menusDescription, index+3);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION - 1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(menuLink, LINK_MENU_TIMEOUT, menuDetails, menuId);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;br /&gt;
[[Category:LSL Library|Dialog Handler (MultiUser)]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=Smooth_Sliding_Door&amp;diff=1062423</id>
		<title>Smooth Sliding Door</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=Smooth_Sliding_Door&amp;diff=1062423"/>
		<updated>2010-10-14T04:44:26Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL Header}}&lt;br /&gt;
=Smooth Sliding Door Script=&lt;br /&gt;
&lt;br /&gt;
==Introduction==&lt;br /&gt;
It really annoys me that there are so many bad looking doors out there that jerk around.  While you can use llSetPos for a sliding door you have no control at what speed it moves.  This sliding door script i created because i wanted to time my sliding door movements with the door sounds i had.  It uses llMoveToTarget to move the door, with the door temporally turning phantom as it moves.&lt;br /&gt;
&lt;br /&gt;
In laggy sims it will behave a little bit funky .. the door sometimes seeming to zoom off to the side before popping into the correct final position as the door becomes a physical object. However, even with that defect it still looks much better IMHO&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Basic Physical Sliding Door Script&lt;br /&gt;
// by SimonT Quinnell &lt;br /&gt;
// 11/07/2009&lt;br /&gt;
//&lt;br /&gt;
// NOTE: If you are going to reposition the door, do it while the door is closed.&lt;br /&gt;
//  Otherwise it will try and use the old close position as a reference point.&lt;br /&gt;
//&lt;br /&gt;
// Licensed under the &amp;quot;OpenCollar License&amp;quot;&lt;br /&gt;
// Ie. Licensed under the GPLv2, with the additional requirement that these scripts remain &amp;quot;full perms&amp;quot; in Second Life.&lt;br /&gt;
//&lt;br /&gt;
// Edit: Simplified the script to be just a basic move script .. removed the sound triggers (5/1/2010)&lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Movement Constants&lt;br /&gt;
vector      OFFSET = &amp;lt;-2.0, 0.0, 0.0&amp;gt;;      // Directional offset for moving the door in x,y,z coordinates&lt;br /&gt;
float       OPENTIME = 3.5;                 // Time taken to open door&lt;br /&gt;
float       CLOSETIME = 3.5;                // Time taken to close door&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
vector      vPosition;&lt;br /&gt;
rotation    rRot;&lt;br /&gt;
float       omega=0.0;&lt;br /&gt;
vector      vTargetPos;&lt;br /&gt;
integer     bOpen = FALSE;&lt;br /&gt;
integer     bMoving = FALSE;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
MoveDoor()&lt;br /&gt;
{    &lt;br /&gt;
    if(!bOpen)&lt;br /&gt;
    {   // Initial conditions&lt;br /&gt;
        bOpen = TRUE;                &lt;br /&gt;
        rRot = llGetRot();&lt;br /&gt;
        vPosition = llGetPos();&lt;br /&gt;
        &lt;br /&gt;
        // Target Position&lt;br /&gt;
        omega=OPENTIME/llVecDist(&amp;lt;0,0,0&amp;gt;,OFFSET);&lt;br /&gt;
        vTargetPos = vPosition+OFFSET*rRot;&lt;br /&gt;
        &lt;br /&gt;
        // Set the timer to cleanup position&lt;br /&gt;
        llSetTimerEvent(OPENTIME);        &lt;br /&gt;
    }else&lt;br /&gt;
    {        &lt;br /&gt;
        bOpen = FALSE;&lt;br /&gt;
&lt;br /&gt;
        // Target Position&lt;br /&gt;
        omega=CLOSETIME/llVecDist(&amp;lt;0,0,0&amp;gt;,OFFSET);&lt;br /&gt;
        vTargetPos = vPosition;&lt;br /&gt;
        &lt;br /&gt;
        // Set the timer to cleanup position&lt;br /&gt;
        llSetTimerEvent(CLOSETIME);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    // Set Door Physical and move it&lt;br /&gt;
    bMoving = TRUE;&lt;br /&gt;
    llSetStatus(STATUS_PHANTOM, TRUE);&lt;br /&gt;
    llSetStatus(STATUS_PHYSICS, TRUE);&lt;br /&gt;
    llMoveToTarget(vTargetPos,omega);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{            &lt;br /&gt;
    state_entry()&lt;br /&gt;
    {   // Initial conditions&lt;br /&gt;
        rRot = llGetRot();&lt;br /&gt;
        vPosition = llGetPos();&lt;br /&gt;
    }    &lt;br /&gt;
        &lt;br /&gt;
    touch_start(integer num_detected)&lt;br /&gt;
    {&lt;br /&gt;
        MoveDoor();&lt;br /&gt;
    } &lt;br /&gt;
        &lt;br /&gt;
    timer()&lt;br /&gt;
    {&lt;br /&gt;
        // Clean up Position&lt;br /&gt;
        bMoving = FALSE;&lt;br /&gt;
        llSetTimerEvent(0.0);&lt;br /&gt;
        llSetStatus(STATUS_PHYSICS, FALSE);&lt;br /&gt;
        llSetStatus(STATUS_PHANTOM, FALSE);         &lt;br /&gt;
        llSetPrimitiveParams([ PRIM_POSITION, vTargetPos, PRIM_ROTATION, rRot ]);        &lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;br /&gt;
[[Category:LSL Library|Smooth Sliding Door Script]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=Basic_Notecard_Reader&amp;diff=1062413</id>
		<title>Basic Notecard Reader</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=Basic_Notecard_Reader&amp;diff=1062413"/>
		<updated>2010-10-14T04:42:57Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* Introduction */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL Header}}&lt;br /&gt;
=Basic Notecard Reader=&lt;br /&gt;
&lt;br /&gt;
==Introduction==&lt;br /&gt;
&lt;br /&gt;
Quite often i read in configuration settings from a notecard.  I thought i should stick up the core part of my notecard reading script.  It&#039;s not exactly rocket science but hoepfully it&#039;s useful.&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Base Notecard reader&lt;br /&gt;
// &lt;br /&gt;
// by SimonT Quinnell &lt;br /&gt;
//&lt;br /&gt;
//  NOTE.  This treats lines starting with &#039;#&#039; in the notecard as a comment&lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
string  NOTECARD = &amp;quot;notecard&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// internals for reading notecards&lt;br /&gt;
string      strNotecard;&lt;br /&gt;
integer     intLine1;&lt;br /&gt;
key         keyConfigQueryhandle;                   &lt;br /&gt;
key         keyConfigUUID;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{            &lt;br /&gt;
    state_entry()&lt;br /&gt;
    {&lt;br /&gt;
        if (llGetInventoryType(NOTECARD) == INVENTORY_NONE)&lt;br /&gt;
        {&lt;br /&gt;
            llSay(DEBUG_CHANNEL, &amp;quot;The notecard &#039;&amp;quot;+NOTECARD+&amp;quot;&#039;  is missing!&amp;quot;);&lt;br /&gt;
            state main;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            keyConfigQueryhandle = llGetNotecardLine(NOTECARD, intLine1 = 0);&lt;br /&gt;
            keyConfigUUID = llGetInventoryKey(NOTECARD);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    dataserver(key keyQueryId, string strData)&lt;br /&gt;
    {&lt;br /&gt;
        if (keyQueryId == keyConfigQueryhandle)&lt;br /&gt;
        {&lt;br /&gt;
            if (strData != EOF)&lt;br /&gt;
            {              &lt;br /&gt;
                string strToken;&lt;br /&gt;
                strData = llStringTrim(strData, STRING_TRIM_HEAD);      // Trim Whitespace&lt;br /&gt;
                if (llGetSubString (strData, 0, 0) != &amp;quot;#&amp;quot;)              // is it a comment?&lt;br /&gt;
                {&lt;br /&gt;
                    // OK .. now we do somthing in here with our notecard&lt;br /&gt;
                } &lt;br /&gt;
                keyConfigQueryhandle = llGetNotecardLine(strNotecard, ++intLine1);&lt;br /&gt;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                // Finished reasding the notecard&lt;br /&gt;
                state main;&lt;br /&gt;
            }&lt;br /&gt;
        }           &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
state main&lt;br /&gt;
{&lt;br /&gt;
    state_entry()&lt;br /&gt;
    {&lt;br /&gt;
        //&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    changed(integer intChange)         &lt;br /&gt;
    {&lt;br /&gt;
        if (intChange &amp;amp; CHANGED_INVENTORY)&lt;br /&gt;
        {   // If the notecard has changed, then reload the notecard&lt;br /&gt;
            if (keyConfigUUID != llGetInventoryKey(NOTECARD))&lt;br /&gt;
            {&lt;br /&gt;
                state default;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=Basic_Notecard_Reader&amp;diff=1062403</id>
		<title>Basic Notecard Reader</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=Basic_Notecard_Reader&amp;diff=1062403"/>
		<updated>2010-10-14T04:41:25Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* The Script */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL Header}}&lt;br /&gt;
=Basic Notecard Reader=&lt;br /&gt;
&lt;br /&gt;
==Introduction==&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Base Notecard reader&lt;br /&gt;
// &lt;br /&gt;
// by SimonT Quinnell &lt;br /&gt;
//&lt;br /&gt;
//  NOTE.  This treats lines starting with &#039;#&#039; in the notecard as a comment&lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
string  NOTECARD = &amp;quot;notecard&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// internals for reading notecards&lt;br /&gt;
string      strNotecard;&lt;br /&gt;
integer     intLine1;&lt;br /&gt;
key         keyConfigQueryhandle;                   &lt;br /&gt;
key         keyConfigUUID;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{            &lt;br /&gt;
    state_entry()&lt;br /&gt;
    {&lt;br /&gt;
        if (llGetInventoryType(NOTECARD) == INVENTORY_NONE)&lt;br /&gt;
        {&lt;br /&gt;
            llSay(DEBUG_CHANNEL, &amp;quot;The notecard &#039;&amp;quot;+NOTECARD+&amp;quot;&#039;  is missing!&amp;quot;);&lt;br /&gt;
            state main;&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            keyConfigQueryhandle = llGetNotecardLine(NOTECARD, intLine1 = 0);&lt;br /&gt;
            keyConfigUUID = llGetInventoryKey(NOTECARD);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    dataserver(key keyQueryId, string strData)&lt;br /&gt;
    {&lt;br /&gt;
        if (keyQueryId == keyConfigQueryhandle)&lt;br /&gt;
        {&lt;br /&gt;
            if (strData != EOF)&lt;br /&gt;
            {              &lt;br /&gt;
                string strToken;&lt;br /&gt;
                strData = llStringTrim(strData, STRING_TRIM_HEAD);      // Trim Whitespace&lt;br /&gt;
                if (llGetSubString (strData, 0, 0) != &amp;quot;#&amp;quot;)              // is it a comment?&lt;br /&gt;
                {&lt;br /&gt;
                    // OK .. now we do somthing in here with our notecard&lt;br /&gt;
                } &lt;br /&gt;
                keyConfigQueryhandle = llGetNotecardLine(strNotecard, ++intLine1);&lt;br /&gt;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                // Finished reasding the notecard&lt;br /&gt;
                state main;&lt;br /&gt;
            }&lt;br /&gt;
        }           &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
state main&lt;br /&gt;
{&lt;br /&gt;
    state_entry()&lt;br /&gt;
    {&lt;br /&gt;
        //&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    changed(integer intChange)         &lt;br /&gt;
    {&lt;br /&gt;
        if (intChange &amp;amp; CHANGED_INVENTORY)&lt;br /&gt;
        {   // If the notecard has changed, then reload the notecard&lt;br /&gt;
            if (keyConfigUUID != llGetInventoryKey(NOTECARD))&lt;br /&gt;
            {&lt;br /&gt;
                state default;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=Smooth_Sliding_Door&amp;diff=1062393</id>
		<title>Smooth Sliding Door</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=Smooth_Sliding_Door&amp;diff=1062393"/>
		<updated>2010-10-14T04:39:23Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL Header}}&lt;br /&gt;
=Smooth Sliding Door Script=&lt;br /&gt;
&lt;br /&gt;
==Introduction==&lt;br /&gt;
It really annoys me that there are so many bad looking doors out there that jerk around.  While you can use llSetPos for a sliding door you have no control at what speed it moves.  This sliding door script i created because i wanted to time my sliding door movements with the door sounds i had.  It uses llMoveToTarget to move the door, with the door temporally turning phantom as it moves.&lt;br /&gt;
&lt;br /&gt;
In laggy sims it will behave a little bit funky .. the door sometimes seeming to zoom off to the side before popping into the correct final position as the door becomes a physical object. However, even with that defect it still looks much better IMHO&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Basic Physical Sliding Door Script&lt;br /&gt;
// by SimonT Quinnell &lt;br /&gt;
// 11/07/2009&lt;br /&gt;
//&lt;br /&gt;
// NOTE: If you are going to reposition the door, do it while the door is closed.&lt;br /&gt;
//  Otherwise it will try and use the old close position as a reference point.&lt;br /&gt;
//&lt;br /&gt;
// Licensed under the &amp;quot;OpenCollar License&amp;quot;&lt;br /&gt;
// Ie. Licensed under the GPLv2, with the additional requirement that these scripts remain &amp;quot;full perms&amp;quot; in Second Life.&lt;br /&gt;
//&lt;br /&gt;
// Edit: Simplified the script to be just a basic move script .. removed the sound triggers (5/1/2010)&lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Movement Constants&lt;br /&gt;
vector      OFFSET = &amp;lt;-2.0, 0.0, 0.0&amp;gt;;      // Directional offset for moving the door in x,y,z coordinates&lt;br /&gt;
float       OPENTIME = 3.5;                 // Time taken to open door&lt;br /&gt;
float       CLOSETIME = 3.5;                // Time taken to close door&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
vector      vPosition;&lt;br /&gt;
rotation    rRot;&lt;br /&gt;
float       omega=0.0;&lt;br /&gt;
vector      vTargetPos;&lt;br /&gt;
integer     bOpen = FALSE;&lt;br /&gt;
integer     bMoving = FALSE;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
MoveDoor()&lt;br /&gt;
{    &lt;br /&gt;
    if(!bOpen)&lt;br /&gt;
    {   // Initial conditions&lt;br /&gt;
        bOpen = TRUE;                &lt;br /&gt;
        rRot = llGetRot();&lt;br /&gt;
        vPosition = llGetPos();&lt;br /&gt;
        &lt;br /&gt;
        // Target Position&lt;br /&gt;
        omega=OPENTIME/llVecDist(&amp;lt;0,0,0&amp;gt;,OFFSET);&lt;br /&gt;
        vTargetPos = vPosition+OFFSET*rRot;&lt;br /&gt;
        &lt;br /&gt;
        // Set the timer to cleanup position&lt;br /&gt;
        llSetTimerEvent(OPENTIME);        &lt;br /&gt;
    }else&lt;br /&gt;
    {        &lt;br /&gt;
        bOpen = FALSE;&lt;br /&gt;
&lt;br /&gt;
        // Target Position&lt;br /&gt;
        omega=CLOSETIME/llVecDist(&amp;lt;0,0,0&amp;gt;,OFFSET);&lt;br /&gt;
        vTargetPos = vPosition;&lt;br /&gt;
        &lt;br /&gt;
        // Set the timer to cleanup position&lt;br /&gt;
        llSetTimerEvent(CLOSETIME);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    // Set Door Physical and move it&lt;br /&gt;
    bMoving = TRUE;&lt;br /&gt;
    llSetStatus(STATUS_PHANTOM, TRUE);&lt;br /&gt;
    llSetStatus(STATUS_PHYSICS, TRUE);&lt;br /&gt;
    llMoveToTarget(vTargetPos,omega);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{            &lt;br /&gt;
    state_entry()&lt;br /&gt;
    {   // Initial conditions&lt;br /&gt;
        rRot = llGetRot();&lt;br /&gt;
        vPosition = llGetPos();&lt;br /&gt;
    }    &lt;br /&gt;
        &lt;br /&gt;
    touch_start(integer num_detected)&lt;br /&gt;
    {&lt;br /&gt;
        MoveDoor();&lt;br /&gt;
    } &lt;br /&gt;
        &lt;br /&gt;
    timer()&lt;br /&gt;
    {&lt;br /&gt;
        // Clean up Position&lt;br /&gt;
        bMoving = FALSE;&lt;br /&gt;
        llSetTimerEvent(0.0);&lt;br /&gt;
        llSetStatus(STATUS_PHYSICS, FALSE);&lt;br /&gt;
        llSetStatus(STATUS_PHANTOM, FALSE);         &lt;br /&gt;
        llSetPrimitiveParams([ PRIM_POSITION, vTargetPos, PRIM_ROTATION, rRot ]);        &lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=Basic_Notecard_Reader&amp;diff=1062383</id>
		<title>Basic Notecard Reader</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=Basic_Notecard_Reader&amp;diff=1062383"/>
		<updated>2010-10-14T04:39:14Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: Created page with &amp;#039;{{LSL Header}} =Basic Notecard Reader=  ==Introduction==  ==The Script==  ==See also== * Script Library &amp;#039;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL Header}}&lt;br /&gt;
=Basic Notecard Reader=&lt;br /&gt;
&lt;br /&gt;
==Introduction==&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:SimonT_Quinnell&amp;diff=1062373</id>
		<title>User:SimonT Quinnell</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:SimonT_Quinnell&amp;diff=1062373"/>
		<updated>2010-10-14T04:37:20Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;A few random scripts that I have written and are free for anyone to use&lt;br /&gt;
&lt;br /&gt;
* [http://wiki.secondlife.com/wiki/Basic_Notecard_Reader  Notecard Reader]&lt;br /&gt;
* [http://wiki.secondlife.com/wiki/Smooth_Sliding_Door  Smooth Sliding door script]&lt;br /&gt;
* [http://wiki.secondlife.com/wiki/MultiUser_Dialog_Handler  Dialog Handler]&lt;br /&gt;
&lt;br /&gt;
--[[User:SimonT Quinnell|SimonT Quinnell]] 02:39, 1 September 2010 (UTC)&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062363</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062363"/>
		<updated>2010-10-14T04:30:43Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* The Script */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users, scripts and prims.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;FALSE&amp;quot;;      //Navigation buttons not displayed&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;TopLeft~Top~TopRight~Left~Center~Right~BottomLeft~Bottom~BottomRight&amp;quot;;   // The buttons, each button separated by a &#039;~&#039;.&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|TopLeft|| Top|| TopRight&lt;br /&gt;
|-&lt;br /&gt;
|Left|| Center || Right&lt;br /&gt;
|-&lt;br /&gt;
|BottomLeft|| Bottom || BottomRight&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu with Auto-Navigation Buttons===&lt;br /&gt;
In this case we now add navigation buttons to the bottom of the script.  Back and Exit buttons are added to the bottom and if necessary &amp;gt;&amp;gt; and &amp;lt;&amp;lt; buttons.  In the script that is calling the menu I use a function something like this.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;      //The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like this.&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.  Here is the menu call.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            &lt;br /&gt;
            if (menuDescriptor == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
            {    // Is this a response to &amp;quot;mymenu&amp;quot; from the dialog script?&lt;br /&gt;
                 string  item = llList2String(returnMenu,1);             // The actual button pushed&lt;br /&gt;
&lt;br /&gt;
                 // id == the key of the menu user.&lt;br /&gt;
                 // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             if (message == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
             {&lt;br /&gt;
                // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuidentifier | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
// Return is in the format&lt;br /&gt;
// &amp;quot;menuidentifier | item&amp;quot;&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell&lt;br /&gt;
//&lt;br /&gt;
// CHANGES&lt;br /&gt;
// 2010/10/14 - Timeout message now gets sent to the prim that called the menu, not LINK_THIS.  Also includes menuidentifier&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llSay(DEBUG_CHANNEL,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key     menuId = llList2Key(menusDescription, index+1);&lt;br /&gt;
        string  menuDetails = llList2String(menusDescription, index+2);&lt;br /&gt;
        integer menuLink = llList2Integer(menusDescription, index+3);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION - 1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(menuLink, LINK_MENU_TIMEOUT, menuDetails, menuId);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062353</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062353"/>
		<updated>2010-10-14T04:27:13Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* Parsing the returning Menu information */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users, scripts and prims.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;FALSE&amp;quot;;      //Navigation buttons not displayed&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;TopLeft~Top~TopRight~Left~Center~Right~BottomLeft~Bottom~BottomRight&amp;quot;;   // The buttons, each button separated by a &#039;~&#039;.&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|TopLeft|| Top|| TopRight&lt;br /&gt;
|-&lt;br /&gt;
|Left|| Center || Right&lt;br /&gt;
|-&lt;br /&gt;
|BottomLeft|| Bottom || BottomRight&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu with Auto-Navigation Buttons===&lt;br /&gt;
In this case we now add navigation buttons to the bottom of the script.  Back and Exit buttons are added to the bottom and if necessary &amp;gt;&amp;gt; and &amp;lt;&amp;lt; buttons.  In the script that is calling the menu I use a function something like this.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;      //The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like this.&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.  Here is the menu call.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            &lt;br /&gt;
            if (menuDescriptor == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
            {    // Is this a response to &amp;quot;mymenu&amp;quot; from the dialog script?&lt;br /&gt;
                 string  item = llList2String(returnMenu,1);             // The actual button pushed&lt;br /&gt;
&lt;br /&gt;
                 // id == the key of the menu user.&lt;br /&gt;
                 // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             if (message == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
             {&lt;br /&gt;
                // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuidentifier | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
// Return is in the format&lt;br /&gt;
// &amp;quot;menuidentifier | item&amp;quot;&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell&lt;br /&gt;
//&lt;br /&gt;
// CHANGES&lt;br /&gt;
// 2010/10/14 - Timeout message now gets sent to the prim that called the menu, not LINK_THIS.  Also includes menuidentifier&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llWhisper(0,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key     menuId = llList2Key(menusDescription, index+1);&lt;br /&gt;
        string  menuDetails = llList2String(menusDescription, index+2);&lt;br /&gt;
        integer menuLink = llList2Integer(menusDescription, index+3);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION - 1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(menuLink, LINK_MENU_TIMEOUT, menuDetails, menuId);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062343</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062343"/>
		<updated>2010-10-14T04:26:09Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* Calling a Simple Menu */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users, scripts and prims.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;FALSE&amp;quot;;      //Navigation buttons not displayed&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;TopLeft~Top~TopRight~Left~Center~Right~BottomLeft~Bottom~BottomRight&amp;quot;;   // The buttons, each button separated by a &#039;~&#039;.&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|TopLeft|| Top|| TopRight&lt;br /&gt;
|-&lt;br /&gt;
|Left|| Center || Right&lt;br /&gt;
|-&lt;br /&gt;
|BottomLeft|| Bottom || BottomRight&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu with Auto-Navigation Buttons===&lt;br /&gt;
In this case we now add navigation buttons to the bottom of the script.  Back and Exit buttons are added to the bottom and if necessary &amp;gt;&amp;gt; and &amp;lt;&amp;lt; buttons.  In the script that is calling the menu I use a function something like this.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;      //The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like this.&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.  Here is the menu call.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            &lt;br /&gt;
            if (menuDescriptor == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
            {    // Is this my menu that is sending this message?&lt;br /&gt;
                 string  item = llList2String(returnMenu,1);             // The actual button pushed&lt;br /&gt;
&lt;br /&gt;
                 // id == the key of the menu user.&lt;br /&gt;
                 // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             if (message == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
             {&lt;br /&gt;
                // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuidentifier | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
// Return is in the format&lt;br /&gt;
// &amp;quot;menuidentifier | item&amp;quot;&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell&lt;br /&gt;
//&lt;br /&gt;
// CHANGES&lt;br /&gt;
// 2010/10/14 - Timeout message now gets sent to the prim that called the menu, not LINK_THIS.  Also includes menuidentifier&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llWhisper(0,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key     menuId = llList2Key(menusDescription, index+1);&lt;br /&gt;
        string  menuDetails = llList2String(menusDescription, index+2);&lt;br /&gt;
        integer menuLink = llList2Integer(menusDescription, index+3);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION - 1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(menuLink, LINK_MENU_TIMEOUT, menuDetails, menuId);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062333</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062333"/>
		<updated>2010-10-14T04:25:35Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* Calling a Simple Menu with Auto-Navigation Buttons */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users, scripts and prims.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;FALSE&amp;quot;;      //Navigation buttons not displayed&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;TopLeft~Top~TopRight~Left~Center~Right~BottomLeft~Bottom~BottomRight&amp;quot;;   // The buttons, each button separated by a &#039;~&#039;.&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|TopLeft|| Top|| TopRight&lt;br /&gt;
|-&lt;br /&gt;
|Left|| Center || Right&lt;br /&gt;
|-&lt;br /&gt;
|BottomLeft|| Bottom || BottomRight&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu with Auto-Navigation Buttons===&lt;br /&gt;
In this case we now add navigation buttons to the bottom of the script.  Back and Exit buttons are added to the bottom and if necessary &amp;gt;&amp;gt; and &amp;lt;&amp;lt; buttons.  In the script that is calling the menu I use a function something like this.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;      //The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like this.&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.  Here is the menu call.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            &lt;br /&gt;
            if (menuDescriptor == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
            {    // Is this my menu that is sending this message?&lt;br /&gt;
                 string  item = llList2String(returnMenu,1);             // The actual button pushed&lt;br /&gt;
&lt;br /&gt;
                 // id == the key of the menu user.&lt;br /&gt;
                 // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             if (message == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
             {&lt;br /&gt;
                // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuidentifier | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
// Return is in the format&lt;br /&gt;
// &amp;quot;menuidentifier | item&amp;quot;&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell&lt;br /&gt;
//&lt;br /&gt;
// CHANGES&lt;br /&gt;
// 2010/10/14 - Timeout message now gets sent to the prim that called the menu, not LINK_THIS.  Also includes menuidentifier&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llWhisper(0,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key     menuId = llList2Key(menusDescription, index+1);&lt;br /&gt;
        string  menuDetails = llList2String(menusDescription, index+2);&lt;br /&gt;
        integer menuLink = llList2Integer(menusDescription, index+3);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION - 1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(menuLink, LINK_MENU_TIMEOUT, menuDetails, menuId);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062323</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062323"/>
		<updated>2010-10-14T04:25:03Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* Calling a Simple Menu with Auto-Navigation Buttons */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users, scripts and prims.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;FALSE&amp;quot;;      //Navigation buttons not displayed&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;TopLeft~Top~TopRight~Left~Center~Right~BottomLeft~Bottom~BottomRight&amp;quot;;   // The buttons, each button separated by a &#039;~&#039;.&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|TopLeft|| Top|| TopRight&lt;br /&gt;
|-&lt;br /&gt;
|Left|| Center || Right&lt;br /&gt;
|-&lt;br /&gt;
|BottomLeft|| Bottom || BottomRight&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu with Auto-Navigation Buttons===&lt;br /&gt;
In this case we now add navigation buttons to the bottom of the script.  Back and Exit buttons are added to the bootom and if necessary &amp;gt;&amp;gt; and &amp;lt;&amp;lt; buttons.  In the script that is calling the menu I use a function something like this.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;      //The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like this.&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.  Here is the menu call.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            &lt;br /&gt;
            if (menuDescriptor == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
            {    // Is this my menu that is sending this message?&lt;br /&gt;
                 string  item = llList2String(returnMenu,1);             // The actual button pushed&lt;br /&gt;
&lt;br /&gt;
                 // id == the key of the menu user.&lt;br /&gt;
                 // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             if (message == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
             {&lt;br /&gt;
                // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuidentifier | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
// Return is in the format&lt;br /&gt;
// &amp;quot;menuidentifier | item&amp;quot;&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell&lt;br /&gt;
//&lt;br /&gt;
// CHANGES&lt;br /&gt;
// 2010/10/14 - Timeout message now gets sent to the prim that called the menu, not LINK_THIS.  Also includes menuidentifier&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llWhisper(0,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key     menuId = llList2Key(menusDescription, index+1);&lt;br /&gt;
        string  menuDetails = llList2String(menusDescription, index+2);&lt;br /&gt;
        integer menuLink = llList2Integer(menusDescription, index+3);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION - 1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(menuLink, LINK_MENU_TIMEOUT, menuDetails, menuId);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062313</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062313"/>
		<updated>2010-10-14T04:24:46Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* Calling a Complex Menu */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users, scripts and prims.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;FALSE&amp;quot;;      //Navigation buttons not displayed&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;TopLeft~Top~TopRight~Left~Center~Right~BottomLeft~Bottom~BottomRight&amp;quot;;   // The buttons, each button separated by a &#039;~&#039;.&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|TopLeft|| Top|| TopRight&lt;br /&gt;
|-&lt;br /&gt;
|Left|| Center || Right&lt;br /&gt;
|-&lt;br /&gt;
|BottomLeft|| Bottom || BottomRight&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu with Auto-Navigation Buttons===&lt;br /&gt;
In this case we now add navigation buttons to the bottom of the script.  Back and Exit buttons are added to the bootom and if necessary &amp;gt;&amp;gt; and &amp;lt;&amp;lt; buttons.  In the script that is calling the menu I use a function something like this.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;      //The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like this.&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.  Here is the menu call.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            &lt;br /&gt;
            if (menuDescriptor == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
            {    // Is this my menu that is sending this message?&lt;br /&gt;
                 string  item = llList2String(returnMenu,1);             // The actual button pushed&lt;br /&gt;
&lt;br /&gt;
                 // id == the key of the menu user.&lt;br /&gt;
                 // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             if (message == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
             {&lt;br /&gt;
                // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuidentifier | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
// Return is in the format&lt;br /&gt;
// &amp;quot;menuidentifier | item&amp;quot;&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell&lt;br /&gt;
//&lt;br /&gt;
// CHANGES&lt;br /&gt;
// 2010/10/14 - Timeout message now gets sent to the prim that called the menu, not LINK_THIS.  Also includes menuidentifier&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llWhisper(0,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key     menuId = llList2Key(menusDescription, index+1);&lt;br /&gt;
        string  menuDetails = llList2String(menusDescription, index+2);&lt;br /&gt;
        integer menuLink = llList2Integer(menusDescription, index+3);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION - 1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(menuLink, LINK_MENU_TIMEOUT, menuDetails, menuId);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062303</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062303"/>
		<updated>2010-10-14T04:24:30Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* Parsing the returning Menu information */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users, scripts and prims.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;FALSE&amp;quot;;      //Navigation buttons not displayed&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;TopLeft~Top~TopRight~Left~Center~Right~BottomLeft~Bottom~BottomRight&amp;quot;;   // The buttons, each button separated by a &#039;~&#039;.&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|TopLeft|| Top|| TopRight&lt;br /&gt;
|-&lt;br /&gt;
|Left|| Center || Right&lt;br /&gt;
|-&lt;br /&gt;
|BottomLeft|| Bottom || BottomRight&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu with Auto-Navigation Buttons===&lt;br /&gt;
In this case we now add navigation buttons to the bottom of the script.  Back and Exit buttons are added to the bootom and if necessary &amp;gt;&amp;gt; and &amp;lt;&amp;lt; buttons.  In the script that is calling the menu I use a function something like this.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;      //The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like this.&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.  Here is the menu call.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            &lt;br /&gt;
            if (menuDescriptor == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
            {    // Is this my menu that is sending this message?&lt;br /&gt;
                 string  item = llList2String(returnMenu,1);             // The actual button pushed&lt;br /&gt;
&lt;br /&gt;
                 // id == the key of the menu user.&lt;br /&gt;
                 // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             if (message == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
             {&lt;br /&gt;
                // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuidentifier | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
// Return is in the format&lt;br /&gt;
// &amp;quot;menuidentifier | item&amp;quot;&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell&lt;br /&gt;
//&lt;br /&gt;
// CHANGES&lt;br /&gt;
// 2010/10/14 - Timeout message now gets sent to the prim that called the menu, not LINK_THIS.  Also includes menuidentifier&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llWhisper(0,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key     menuId = llList2Key(menusDescription, index+1);&lt;br /&gt;
        string  menuDetails = llList2String(menusDescription, index+2);&lt;br /&gt;
        integer menuLink = llList2Integer(menusDescription, index+3);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION - 1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(menuLink, LINK_MENU_TIMEOUT, menuDetails, menuId);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062293</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062293"/>
		<updated>2010-10-14T04:23:15Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* Parsing the returning Menu information */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users, scripts and prims.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;FALSE&amp;quot;;      //Navigation buttons not displayed&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;TopLeft~Top~TopRight~Left~Center~Right~BottomLeft~Bottom~BottomRight&amp;quot;;   // The buttons, each button separated by a &#039;~&#039;.&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|TopLeft|| Top|| TopRight&lt;br /&gt;
|-&lt;br /&gt;
|Left|| Center || Right&lt;br /&gt;
|-&lt;br /&gt;
|BottomLeft|| Bottom || BottomRight&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu with Auto-Navigation Buttons===&lt;br /&gt;
In this case we now add navigation buttons to the bottom of the script.  Back and Exit buttons are added to the bootom and if necessary &amp;gt;&amp;gt; and &amp;lt;&amp;lt; buttons.  In the script that is calling the menu I use a function something like this.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;      //The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like this.&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.  Here is the menu call.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            &lt;br /&gt;
            if (menuDescriptor == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
            {    // Is this my menu that is sending this message?&lt;br /&gt;
                 string  item = llList2String(returnMenu,1);             // The actual button pushed&lt;br /&gt;
&lt;br /&gt;
                 // id == the key of the menu user.&lt;br /&gt;
                 // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             if (message == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
             {&lt;br /&gt;
                // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuidentifier | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
// Return is in the format&lt;br /&gt;
// &amp;quot;menuidentifier | item&amp;quot;&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell&lt;br /&gt;
//&lt;br /&gt;
// CHANGES&lt;br /&gt;
// 2010/10/14 - Timeout message now gets sent to the prim that called the menu, not LINK_THIS.  Also includes menuidentifier&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llWhisper(0,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key     menuId = llList2Key(menusDescription, index+1);&lt;br /&gt;
        string  menuDetails = llList2String(menusDescription, index+2);&lt;br /&gt;
        integer menuLink = llList2Integer(menusDescription, index+3);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION - 1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(menuLink, LINK_MENU_TIMEOUT, menuDetails, menuId);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062283</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062283"/>
		<updated>2010-10-14T04:21:11Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* Calling a Complex Menu */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users, scripts and prims.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;FALSE&amp;quot;;      //Navigation buttons not displayed&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;TopLeft~Top~TopRight~Left~Center~Right~BottomLeft~Bottom~BottomRight&amp;quot;;   // The buttons, each button separated by a &#039;~&#039;.&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|TopLeft|| Top|| TopRight&lt;br /&gt;
|-&lt;br /&gt;
|Left|| Center || Right&lt;br /&gt;
|-&lt;br /&gt;
|BottomLeft|| Bottom || BottomRight&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu with Auto-Navigation Buttons===&lt;br /&gt;
In this case we now add navigation buttons to the bottom of the script.  Back and Exit buttons are added to the bootom and if necessary &amp;gt;&amp;gt; and &amp;lt;&amp;lt; buttons.  In the script that is calling the menu I use a function something like this.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;      //The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like this.&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.  Here is the menu call.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            &lt;br /&gt;
            if (menuDescriptor == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
            {    // Is this my menu that is sending this message?&lt;br /&gt;
                 string  item = llList2String(returnMenu,1);             // The actual button pushed&lt;br /&gt;
&lt;br /&gt;
                 // id == the key of the menu user.&lt;br /&gt;
                 // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuidentifier | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
// Return is in the format&lt;br /&gt;
// &amp;quot;menuidentifier | item&amp;quot;&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell&lt;br /&gt;
//&lt;br /&gt;
// CHANGES&lt;br /&gt;
// 2010/10/14 - Timeout message now gets sent to the prim that called the menu, not LINK_THIS.  Also includes menuidentifier&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llWhisper(0,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key     menuId = llList2Key(menusDescription, index+1);&lt;br /&gt;
        string  menuDetails = llList2String(menusDescription, index+2);&lt;br /&gt;
        integer menuLink = llList2Integer(menusDescription, index+3);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION - 1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(menuLink, LINK_MENU_TIMEOUT, menuDetails, menuId);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062273</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062273"/>
		<updated>2010-10-14T04:19:43Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* Calling a Simple Menu with Auto-Navigation Buttons */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users, scripts and prims.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;FALSE&amp;quot;;      //Navigation buttons not displayed&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;TopLeft~Top~TopRight~Left~Center~Right~BottomLeft~Bottom~BottomRight&amp;quot;;   // The buttons, each button separated by a &#039;~&#039;.&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|TopLeft|| Top|| TopRight&lt;br /&gt;
|-&lt;br /&gt;
|Left|| Center || Right&lt;br /&gt;
|-&lt;br /&gt;
|BottomLeft|| Bottom || BottomRight&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu with Auto-Navigation Buttons===&lt;br /&gt;
In this case we now add navigation buttons to the bottom of the script.  Back and Exit buttons are added to the bootom and if necessary &amp;gt;&amp;gt; and &amp;lt;&amp;lt; buttons.  In the script that is calling the menu I use a function something like this.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;      //The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like this.&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
The above case is a simple menu.  The script can also do more complex menus.  &lt;br /&gt;
&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            &lt;br /&gt;
            if (menuDescriptor == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
            {    // Is this my menu that is sending this message?&lt;br /&gt;
                 string  item = llList2String(returnMenu,1);             // The actual button pushed&lt;br /&gt;
&lt;br /&gt;
                 // id == the key of the menu user.&lt;br /&gt;
                 // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuidentifier | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
// Return is in the format&lt;br /&gt;
// &amp;quot;menuidentifier | item&amp;quot;&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell&lt;br /&gt;
//&lt;br /&gt;
// CHANGES&lt;br /&gt;
// 2010/10/14 - Timeout message now gets sent to the prim that called the menu, not LINK_THIS.  Also includes menuidentifier&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llWhisper(0,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key     menuId = llList2Key(menusDescription, index+1);&lt;br /&gt;
        string  menuDetails = llList2String(menusDescription, index+2);&lt;br /&gt;
        integer menuLink = llList2Integer(menusDescription, index+3);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION - 1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(menuLink, LINK_MENU_TIMEOUT, menuDetails, menuId);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062263</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062263"/>
		<updated>2010-10-14T04:18:10Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* Calling a Simple Menu */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users, scripts and prims.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;FALSE&amp;quot;;      //Navigation buttons not displayed&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;TopLeft~Top~TopRight~Left~Center~Right~BottomLeft~Bottom~BottomRight&amp;quot;;   // The buttons, each button separated by a &#039;~&#039;.&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|TopLeft|| Top|| TopRight&lt;br /&gt;
|-&lt;br /&gt;
|Left|| Center || Right&lt;br /&gt;
|-&lt;br /&gt;
|BottomLeft|| Bottom || BottomRight&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu with Auto-Navigation Buttons===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.  This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;      //The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
The above case is a simple menu.  The script can also do more complex menus.  &lt;br /&gt;
&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            &lt;br /&gt;
            if (menuDescriptor == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
            {    // Is this my menu that is sending this message?&lt;br /&gt;
                 string  item = llList2String(returnMenu,1);             // The actual button pushed&lt;br /&gt;
&lt;br /&gt;
                 // id == the key of the menu user.&lt;br /&gt;
                 // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuidentifier | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
// Return is in the format&lt;br /&gt;
// &amp;quot;menuidentifier | item&amp;quot;&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell&lt;br /&gt;
//&lt;br /&gt;
// CHANGES&lt;br /&gt;
// 2010/10/14 - Timeout message now gets sent to the prim that called the menu, not LINK_THIS.  Also includes menuidentifier&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llWhisper(0,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key     menuId = llList2Key(menusDescription, index+1);&lt;br /&gt;
        string  menuDetails = llList2String(menusDescription, index+2);&lt;br /&gt;
        integer menuLink = llList2Integer(menusDescription, index+3);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION - 1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(menuLink, LINK_MENU_TIMEOUT, menuDetails, menuId);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062253</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062253"/>
		<updated>2010-10-14T04:13:57Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users, scripts and prims.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.  This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|TopLeft|| Top|| TopRight&lt;br /&gt;
|-&lt;br /&gt;
|Left|| Center || Right&lt;br /&gt;
|-&lt;br /&gt;
|BottomLeft|| Bottom || BottomRight&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;FALSE&amp;quot;;      //Navigation buttons not displayed&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;TopLeft~Top~TopRight~Left~Center~Right~BottomLeft~Bottom~BottomRight&amp;quot;;   // The buttons, each button separated by a &#039;~&#039;.&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu with Auto-Navigation Buttons===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.  This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;      //The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
The above case is a simple menu.  The script can also do more complex menus.  &lt;br /&gt;
&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            &lt;br /&gt;
            if (menuDescriptor == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
            {    // Is this my menu that is sending this message?&lt;br /&gt;
                 string  item = llList2String(returnMenu,1);             // The actual button pushed&lt;br /&gt;
&lt;br /&gt;
                 // id == the key of the menu user.&lt;br /&gt;
                 // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuidentifier | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
// Return is in the format&lt;br /&gt;
// &amp;quot;menuidentifier | item&amp;quot;&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell&lt;br /&gt;
//&lt;br /&gt;
// CHANGES&lt;br /&gt;
// 2010/10/14 - Timeout message now gets sent to the prim that called the menu, not LINK_THIS.  Also includes menuidentifier&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llWhisper(0,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key     menuId = llList2Key(menusDescription, index+1);&lt;br /&gt;
        string  menuDetails = llList2String(menusDescription, index+2);&lt;br /&gt;
        integer menuLink = llList2Integer(menusDescription, index+3);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION - 1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(menuLink, LINK_MENU_TIMEOUT, menuDetails, menuId);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062243</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062243"/>
		<updated>2010-10-14T04:10:50Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users, scripts and prims.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.  This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;FALSE&amp;quot;;      //Navigation buttons not displayed&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu with Auto-Navigation Buttons===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.  This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;      //The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
The above case is a simple menu.  The script can also do more complex menus.  &lt;br /&gt;
&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            &lt;br /&gt;
            if (menuDescriptor == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
            {    // Is this my menu that is sending this message?&lt;br /&gt;
                 string  item = llList2String(returnMenu,1);             // The actual button pushed&lt;br /&gt;
&lt;br /&gt;
                 // id == the key of the menu user.&lt;br /&gt;
                 // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuidentifier | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
// Return is in the format&lt;br /&gt;
// &amp;quot;menuidentifier | item&amp;quot;&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell&lt;br /&gt;
//&lt;br /&gt;
// CHANGES&lt;br /&gt;
// 2010/10/14 - Timeout message now gets sent to the prim that called the menu, not LINK_THIS.  Also includes menuidentifier&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llWhisper(0,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key     menuId = llList2Key(menusDescription, index+1);&lt;br /&gt;
        string  menuDetails = llList2String(menusDescription, index+2);&lt;br /&gt;
        integer menuLink = llList2Integer(menusDescription, index+3);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION - 1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(menuLink, LINK_MENU_TIMEOUT, menuDetails, menuId);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062233</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062233"/>
		<updated>2010-10-14T04:09:01Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users, scripts and prims.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu with Auto-Navigation Buttons===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.  This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;      //The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
The above case is a simple menu.  The script can also do more complex menus.  &lt;br /&gt;
&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            &lt;br /&gt;
            if (menuDescriptor == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
            {    // Is this my menu that is sending this message?&lt;br /&gt;
                 string  item = llList2String(returnMenu,1);             // The actual button pushed&lt;br /&gt;
&lt;br /&gt;
                 // id == the key of the menu user.&lt;br /&gt;
                 // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuidentifier | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
// Return is in the format&lt;br /&gt;
// &amp;quot;menuidentifier | item&amp;quot;&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell&lt;br /&gt;
//&lt;br /&gt;
// CHANGES&lt;br /&gt;
// 2010/10/14 - Timeout message now gets sent to the prim that called the menu, not LINK_THIS.  Also includes menuidentifier&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llWhisper(0,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key     menuId = llList2Key(menusDescription, index+1);&lt;br /&gt;
        string  menuDetails = llList2String(menusDescription, index+2);&lt;br /&gt;
        integer menuLink = llList2Integer(menusDescription, index+3);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION - 1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(menuLink, LINK_MENU_TIMEOUT, menuDetails, menuId);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062223</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062223"/>
		<updated>2010-10-14T04:06:22Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* The Script */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users, scripts and prims.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.  This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;      //The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
The above case is a simple menu.  The script can also do more complex menus.  &lt;br /&gt;
&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            &lt;br /&gt;
            if (menuDescriptor == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
            {    // Is this my menu that is sending this message?&lt;br /&gt;
                 string  item = llList2String(returnMenu,1);             // The actual button pushed&lt;br /&gt;
&lt;br /&gt;
                 // id == the key of the menu user.&lt;br /&gt;
                 // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuidentifier | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
// Return is in the format&lt;br /&gt;
// &amp;quot;menuidentifier | item&amp;quot;&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell&lt;br /&gt;
//&lt;br /&gt;
// CHANGES&lt;br /&gt;
// 2010/10/14 - Timeout message now gets sent to the prim that called the menu, not LINK_THIS.  Also includes menuidentifier&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llWhisper(0,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key     menuId = llList2Key(menusDescription, index+1);&lt;br /&gt;
        string  menuDetails = llList2String(menusDescription, index+2);&lt;br /&gt;
        integer menuLink = llList2Integer(menusDescription, index+3);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION - 1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(menuLink, LINK_MENU_TIMEOUT, menuDetails, menuId);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062213</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062213"/>
		<updated>2010-10-14T04:04:26Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* The Script */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users, scripts and prims.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.  This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;      //The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
The above case is a simple menu.  The script can also do more complex menus.  &lt;br /&gt;
&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            &lt;br /&gt;
            if (menuDescriptor == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
            {    // Is this my menu that is sending this message?&lt;br /&gt;
                 string  item = llList2String(returnMenu,1);             // The actual button pushed&lt;br /&gt;
&lt;br /&gt;
                 // id == the key of the menu user.&lt;br /&gt;
                 // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuidentifier | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
// Return is in the format&lt;br /&gt;
// &amp;quot;menuidentifier | item&amp;quot;&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell &lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llWhisper(0,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key     menuId = llList2Key(menusDescription, index+1);&lt;br /&gt;
        string  menuDetails = llList2String(menusDescription, index+2);&lt;br /&gt;
        integer menuLink = llList2Integer(menusDescription, index+3);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION - 1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(menuLink, LINK_MENU_TIMEOUT, menuDetails, menuId);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062162</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062162"/>
		<updated>2010-10-14T02:41:05Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* Introduction */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users, scripts and prims.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.  This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;      //The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
The above case is a simple menu.  The script can also do more complex menus.  &lt;br /&gt;
&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            &lt;br /&gt;
            if (menuDescriptor == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
            {    // Is this my menu that is sending this message?&lt;br /&gt;
                 string  item = llList2String(returnMenu,1);             // The actual button pushed&lt;br /&gt;
&lt;br /&gt;
                 // id == the key of the menu user.&lt;br /&gt;
                 // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuidentifier | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
// Return is in the format&lt;br /&gt;
// &amp;quot;menuidentifier | item&amp;quot;&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell &lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llWhisper(0,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key id = llList2Key(menusDescription, index+1);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION -1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(LINK_THIS, LINK_MENU_TIMEOUT, &amp;quot;Timeout&amp;quot;, id);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062152</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062152"/>
		<updated>2010-10-14T02:38:02Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* Calling a Simple Menu */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.  This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script. I often include the script name plus menu name.&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;      //The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
The above case is a simple menu.  The script can also do more complex menus.  &lt;br /&gt;
&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            &lt;br /&gt;
            if (menuDescriptor == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
            {    // Is this my menu that is sending this message?&lt;br /&gt;
                 string  item = llList2String(returnMenu,1);             // The actual button pushed&lt;br /&gt;
&lt;br /&gt;
                 // id == the key of the menu user.&lt;br /&gt;
                 // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuidentifier | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
// Return is in the format&lt;br /&gt;
// &amp;quot;menuidentifier | item&amp;quot;&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell &lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llWhisper(0,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key id = llList2Key(menusDescription, index+1);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION -1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(LINK_THIS, LINK_MENU_TIMEOUT, &amp;quot;Timeout&amp;quot;, id);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062142</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062142"/>
		<updated>2010-10-14T02:33:48Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* The Script */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.  This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script.&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;      //The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
The above case is a simple menu.  The script can also do more complex menus.  &lt;br /&gt;
&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            &lt;br /&gt;
            if (menuDescriptor == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
            {    // Is this my menu that is sending this message?&lt;br /&gt;
                 string  item = llList2String(returnMenu,1);             // The actual button pushed&lt;br /&gt;
&lt;br /&gt;
                 // id == the key of the menu user.&lt;br /&gt;
                 // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuidentifier | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
// Return is in the format&lt;br /&gt;
// &amp;quot;menuidentifier | item&amp;quot;&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell &lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llWhisper(0,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key id = llList2Key(menusDescription, index+1);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION -1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(LINK_THIS, LINK_MENU_TIMEOUT, &amp;quot;Timeout&amp;quot;, id);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062132</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062132"/>
		<updated>2010-10-14T02:31:35Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* Calling a Complex Menu */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.  This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script.&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;      //The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
The above case is a simple menu.  The script can also do more complex menus.  &lt;br /&gt;
&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            &lt;br /&gt;
            if (menuDescriptor == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
            {    // Is this my menu that is sending this message?&lt;br /&gt;
                 string  item = llList2String(returnMenu,1);             // The actual button pushed&lt;br /&gt;
&lt;br /&gt;
                 // id == the key of the menu user.&lt;br /&gt;
                 // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuname ~ menuparent | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell &lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llWhisper(0,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key id = llList2Key(menusDescription, index+1);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION -1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(LINK_THIS, LINK_MENU_TIMEOUT, &amp;quot;Timeout&amp;quot;, id);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062122</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062122"/>
		<updated>2010-10-14T02:30:37Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* Calling a Simple Menu */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.  This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu&amp;quot;;  //An identifier to show what menu called the script.&lt;br /&gt;
    string  menuNavigate = &amp;quot;TRUE&amp;quot;;      //The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuNavigate+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
The above case is a simple menu.  The script can also do more complex menus.  &lt;br /&gt;
&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu|TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            &lt;br /&gt;
            if (menuDescriptor == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
            {    // Is this my menu that is sending this message?&lt;br /&gt;
                 string  item = llList2String(returnMenu,1);             // The actual button pushed&lt;br /&gt;
&lt;br /&gt;
                 // id == the key of the menu user.&lt;br /&gt;
                 // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuname ~ menuparent | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell &lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llWhisper(0,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key id = llList2Key(menusDescription, index+1);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION -1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(LINK_THIS, LINK_MENU_TIMEOUT, &amp;quot;Timeout&amp;quot;, id);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062112</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062112"/>
		<updated>2010-10-14T02:29:06Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* Calling a Simple Menu */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.  This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu|TRUE&amp;quot;;  //An identifier to show what menu called the script.  The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
The above case is a simple menu.  The script can also do more complex menus.  &lt;br /&gt;
&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu|TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            &lt;br /&gt;
            if (menuDescriptor == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
            {    // Is this my menu that is sending this message?&lt;br /&gt;
                 string  item = llList2String(returnMenu,1);             // The actual button pushed&lt;br /&gt;
&lt;br /&gt;
                 // id == the key of the menu user.&lt;br /&gt;
                 // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuname ~ menuparent | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell &lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llWhisper(0,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key id = llList2Key(menusDescription, index+1);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION -1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(LINK_THIS, LINK_MENU_TIMEOUT, &amp;quot;Timeout&amp;quot;, id);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062102</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062102"/>
		<updated>2010-10-14T02:28:44Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* Calling a Complex Menu */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.  This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;menudescriptor|TRUE&amp;quot;;  //An identifier to show what menu called the script.  The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
The above case is a simple menu.  The script can also do more complex menus.  &lt;br /&gt;
&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenu|TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            &lt;br /&gt;
            if (menuDescriptor == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
            {    // Is this my menu that is sending this message?&lt;br /&gt;
                 string  item = llList2String(returnMenu,1);             // The actual button pushed&lt;br /&gt;
&lt;br /&gt;
                 // id == the key of the menu user.&lt;br /&gt;
                 // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuname ~ menuparent | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell &lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llWhisper(0,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key id = llList2Key(menusDescription, index+1);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION -1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(LINK_THIS, LINK_MENU_TIMEOUT, &amp;quot;Timeout&amp;quot;, id);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062092</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062092"/>
		<updated>2010-10-14T02:28:25Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* Calling a Complex Menu */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.  This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;menudescriptor|TRUE&amp;quot;;  //An identifier to show what menu called the script.  The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
The above case is a simple menu.  The script can also do more complex menus.  &lt;br /&gt;
&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;menutext|TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            &lt;br /&gt;
            if (menuDescriptor == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
            {    // Is this my menu that is sending this message?&lt;br /&gt;
                 string  item = llList2String(returnMenu,1);             // The actual button pushed&lt;br /&gt;
&lt;br /&gt;
                 // id == the key of the menu user.&lt;br /&gt;
                 // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuname ~ menuparent | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell &lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llWhisper(0,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key id = llList2Key(menusDescription, index+1);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION -1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(LINK_THIS, LINK_MENU_TIMEOUT, &amp;quot;Timeout&amp;quot;, id);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062082</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062082"/>
		<updated>2010-10-14T02:27:54Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: Undo revision 1062072 by SimonT Quinnell (Talk)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.  This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;menudescriptor|TRUE&amp;quot;;  //An identifier to show what menu called the script.  The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
The above case is a simple menu.  The script can also do more complex menus.  &lt;br /&gt;
&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;menudescriptor|TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            &lt;br /&gt;
            if (menuDescriptor == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
            {    // Is this my menu that is sending this message?&lt;br /&gt;
                 string  item = llList2String(returnMenu,1);             // The actual button pushed&lt;br /&gt;
&lt;br /&gt;
                 // id == the key of the menu user.&lt;br /&gt;
                 // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuname ~ menuparent | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell &lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llWhisper(0,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key id = llList2Key(menusDescription, index+1);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION -1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(LINK_THIS, LINK_MENU_TIMEOUT, &amp;quot;Timeout&amp;quot;, id);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062072</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062072"/>
		<updated>2010-10-14T02:27:05Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* Calling a Complex Menu */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.  This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;menudescriptor|TRUE&amp;quot;;  //An identifier to show what menu called the script.  The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
The above case is a simple menu.  The script can also do more complex menus.  &lt;br /&gt;
&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;mymenuext displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            &lt;br /&gt;
            if (menuDescriptor == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
            {    // Is this my menu that is sending this message?&lt;br /&gt;
                 string  item = llList2String(returnMenu,1);             // The actual button pushed&lt;br /&gt;
&lt;br /&gt;
                 // id == the key of the menu user.&lt;br /&gt;
                 // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuname ~ menuparent | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell &lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llWhisper(0,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key id = llList2Key(menusDescription, index+1);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION -1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(LINK_THIS, LINK_MENU_TIMEOUT, &amp;quot;Timeout&amp;quot;, id);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062062</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062062"/>
		<updated>2010-10-14T02:26:33Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* Parsing the returning Menu information */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.  This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;menudescriptor|TRUE&amp;quot;;  //An identifier to show what menu called the script.  The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
The above case is a simple menu.  The script can also do more complex menus.  &lt;br /&gt;
&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;menudescriptor|TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            &lt;br /&gt;
            if (menuDescriptor == &amp;quot;mymenu&amp;quot;)&lt;br /&gt;
            {    // Is this my menu that is sending this message?&lt;br /&gt;
                 string  item = llList2String(returnMenu,1);             // The actual button pushed&lt;br /&gt;
&lt;br /&gt;
                 // id == the key of the menu user.&lt;br /&gt;
                 // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuname ~ menuparent | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell &lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llWhisper(0,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key id = llList2Key(menusDescription, index+1);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION -1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(LINK_THIS, LINK_MENU_TIMEOUT, &amp;quot;Timeout&amp;quot;, id);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062052</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062052"/>
		<updated>2010-10-14T02:23:32Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* Calling a Simple Menu */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.  This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;menudescriptor|TRUE&amp;quot;;  //An identifier to show what menu called the script.  The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
The above case is a simple menu.  The script can also do more complex menus.  &lt;br /&gt;
&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;menudescriptor|TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            string  item = llList2String(returnMenu,1);                  // The actual button pushed&lt;br /&gt;
            &lt;br /&gt;
            // id == the key of the menu user.&lt;br /&gt;
            // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuname ~ menuparent | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell &lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llWhisper(0,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key id = llList2Key(menusDescription, index+1);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION -1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(LINK_THIS, LINK_MENU_TIMEOUT, &amp;quot;Timeout&amp;quot;, id);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062042</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062042"/>
		<updated>2010-10-14T02:22:15Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* Calling a Simple Menu */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.  This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;menudescriptor|TRUE&amp;quot;;  //Menudescriptor is some text i use to identify what menu called the script.  The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text ( will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
The above case is a simple menu.  The script can also do more complex menus.  &lt;br /&gt;
&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;menudescriptor|TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            string  item = llList2String(returnMenu,1);                  // The actual button pushed&lt;br /&gt;
            &lt;br /&gt;
            // id == the key of the menu user.&lt;br /&gt;
            // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuname ~ menuparent | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell &lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llWhisper(0,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key id = llList2Key(menusDescription, index+1);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION -1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(LINK_THIS, LINK_MENU_TIMEOUT, &amp;quot;Timeout&amp;quot;, id);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062032</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062032"/>
		<updated>2010-10-14T02:20:46Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* Parsing the returning Menu information */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.  This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;menudescriptor|TRUE&amp;quot;;  //Menu name is &#039;menu with a parent menu name &#039;parent&#039;.  The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text ( will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
The above case is a simple menu.  The script can also do more complex menus.  &lt;br /&gt;
&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;menudescriptor|TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
            string  menuDescriptor = llList2String(returnMenu,0);        // The idenfifying descriptor.  I sometimes use the name of the script plus menu and parent menu name    &lt;br /&gt;
            string  item = llList2String(returnMenu,1);                  // The actual button pushed&lt;br /&gt;
            &lt;br /&gt;
            // id == the key of the menu user.&lt;br /&gt;
            // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuname ~ menuparent | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell &lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llWhisper(0,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key id = llList2Key(menusDescription, index+1);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION -1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(LINK_THIS, LINK_MENU_TIMEOUT, &amp;quot;Timeout&amp;quot;, id);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062022</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062022"/>
		<updated>2010-10-14T02:18:50Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* Calling a Complex Menu */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.  This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;menudescriptor|TRUE&amp;quot;;  //Menu name is &#039;menu with a parent menu name &#039;parent&#039;.  The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text ( will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
The above case is a simple menu.  The script can also do more complex menus.  &lt;br /&gt;
&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;menudescriptor|TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            list    temp = llParseString2List(llList2String(returnMenu,0), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
            string  menu = llList2String(temp,0);             // The name of the menu    &lt;br /&gt;
            string  parent = llList2String(temp,1);           // The parent of the menu&lt;br /&gt;
            string  item = llList2String(returnMenu,1);       // The actual button pushed&lt;br /&gt;
            &lt;br /&gt;
            // id == the key of the menu user.&lt;br /&gt;
            // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuname ~ menuparent | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell &lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llWhisper(0,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key id = llList2Key(menusDescription, index+1);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION -1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(LINK_THIS, LINK_MENU_TIMEOUT, &amp;quot;Timeout&amp;quot;, id);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062012</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1062012"/>
		<updated>2010-10-14T02:18:26Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* Calling a Simple Menu */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.  This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;menudescriptor|TRUE&amp;quot;;  //Menu name is &#039;menu with a parent menu name &#039;parent&#039;.  The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text ( will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
The above case is a simple menu.  The script can also do more complex menus.  &lt;br /&gt;
&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;menu~parent|TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            list    temp = llParseString2List(llList2String(returnMenu,0), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
            string  menu = llList2String(temp,0);             // The name of the menu    &lt;br /&gt;
            string  parent = llList2String(temp,1);           // The parent of the menu&lt;br /&gt;
            string  item = llList2String(returnMenu,1);       // The actual button pushed&lt;br /&gt;
            &lt;br /&gt;
            // id == the key of the menu user.&lt;br /&gt;
            // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuname ~ menuparent | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell &lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llWhisper(0,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key id = llList2Key(menusDescription, index+1);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION -1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(LINK_THIS, LINK_MENU_TIMEOUT, &amp;quot;Timeout&amp;quot;, id);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1021623</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1021623"/>
		<updated>2010-09-01T06:52:41Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* Parsing the returning Menu information */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.  This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;menu~parent|TRUE&amp;quot;;  //Menu name is &#039;menu with a parent menu name &#039;parent&#039;.  The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text ( will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
The above case is a simple menu.  The script can also do more complex menus.  &lt;br /&gt;
&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;menu~parent|TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            list    temp = llParseString2List(llList2String(returnMenu,0), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
            string  menu = llList2String(temp,0);             // The name of the menu    &lt;br /&gt;
            string  parent = llList2String(temp,1);           // The parent of the menu&lt;br /&gt;
            string  item = llList2String(returnMenu,1);       // The actual button pushed&lt;br /&gt;
            &lt;br /&gt;
            // id == the key of the menu user.&lt;br /&gt;
            // The button pressed in the menu is &#039;item&#039;, so now do something with it&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuname ~ menuparent | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell &lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llWhisper(0,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key id = llList2Key(menusDescription, index+1);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION -1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(LINK_THIS, LINK_MENU_TIMEOUT, &amp;quot;Timeout&amp;quot;, id);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1021613</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1021613"/>
		<updated>2010-09-01T06:51:44Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* Parsing the returning Menu information */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.  This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;menu~parent|TRUE&amp;quot;;  //Menu name is &#039;menu with a parent menu name &#039;parent&#039;.  The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text ( will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
The above case is a simple menu.  The script can also do more complex menus.  &lt;br /&gt;
&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;menu~parent|TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            list    temp = llParseString2List(llList2String(returnMenu,0), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
            string  menu = llList2String(temp,0);             // The name of the menu    &lt;br /&gt;
            string  parent = llList2String(temp,1);           // The parent of the menu&lt;br /&gt;
            string  item = llList2String(returnMenu,1);       // The actual button pushed&lt;br /&gt;
            &lt;br /&gt;
            // id == the key of the menu user.&lt;br /&gt;
            // now do something with your response which is the variable &#039;item&#039;&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuname ~ menuparent | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell &lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llWhisper(0,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key id = llList2Key(menusDescription, index+1);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION -1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(LINK_THIS, LINK_MENU_TIMEOUT, &amp;quot;Timeout&amp;quot;, id);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=Category:LSL_Library&amp;diff=1021603</id>
		<title>Category:LSL Library</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=Category:LSL_Library&amp;diff=1021603"/>
		<updated>2010-09-01T05:20:51Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* LSL Script Library */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL Header|ml=*}}{{RightToc}}&lt;br /&gt;
==Script Library==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;box&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 0.5em 0.5em 1.5em&amp;quot;&amp;gt;&lt;br /&gt;
Come to this page to see complex examples that show how to combine parts of LSL.&lt;br /&gt;
&lt;br /&gt;
Go to the [[:Category:LSL Examples|LSL Examples]] page to see brief examples of how to use parts of LSL &lt;br /&gt;
&lt;br /&gt;
Why collect complex examples here? Well, ...&lt;br /&gt;
&lt;br /&gt;
There are many [[script|scripts]] that have become buried in the [http://forums-archive.secondlife.com/15/1.html Scripting Library archive] or the newer [https://blogs.secondlife.com/community/forums/scripting_library?view=discussions Scripting Library forum], were lost with the death of the early scripting forums, or sit idle in [[inventory|inventories]] that could be useful and should be more accessible.&lt;br /&gt;
&lt;br /&gt;
Other scripters may be in the same situation. This wiki is a well-suited medium for a script library. Feel free to add your scripts to the script library by creating new pages for them and linking to those pages here.&lt;br /&gt;
&lt;br /&gt;
Note that there are many more scripts in the LSL Library here, but you can&#039;t get to them if you don&#039;t know they exist, because they are subpages now, instead of an automatically updated category.  Good luck searching.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;box&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Rules for posting: ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 0.5em&amp;quot;&amp;gt;&lt;br /&gt;
#Your script must be tested and working. If it&#039;s not, stick it in your user-space until it is. This is a list of working, usable scripts.&lt;br /&gt;
#Add a link to your script&#039;s page here. Link back to this page from your script&#039;s page. Start your page with &amp;lt;nowiki&amp;gt;{{LSL Header}}&amp;lt;/nowiki&amp;gt;.&lt;br /&gt;
#Do not add scripts that duplicate the same functionality as an existing script or built in {{LSLGC|Functions|function}}. If yours does, explain why.&lt;br /&gt;
#Do not list simple scripts here. Include those among the [[:Category:LSL Examples|LSL Examples]] instead.&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;box&amp;quot;&amp;gt;&lt;br /&gt;
==LSL Script Library==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;sortable&amp;quot; {{Prettytable}}&lt;br /&gt;
|- {{Hl2}}&lt;br /&gt;
! &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Creator&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Description&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
||[[1st necessity of SL]]&lt;br /&gt;
||[[User:Beer Dailey|Beer Dailey]]&lt;br /&gt;
||Monitors for avatars and (de)activates scripts states to control script performance/lag.&lt;br /&gt;
|-&lt;br /&gt;
||[[3D Radar]]&lt;br /&gt;
||[[User:Jesse Barnett|Jesse Barnett]]&lt;br /&gt;
||Rezzes a ball for each avatar in range. Each ball tracks its own AV and displays distance.&lt;br /&gt;
|-&lt;br /&gt;
||[[Access (NewAge)]]&lt;br /&gt;
||[[User:Asia Snowfall|Asia Snowfall]]&lt;br /&gt;
||An easy to use script for permissions on who can use the script, Public/Group/Owner&lt;br /&gt;
|-&lt;br /&gt;
||[[Aim Detection]]&lt;br /&gt;
||[[User:Han Shuffle|Han Shuffle]]&lt;br /&gt;
||Monitors for avatars and reports back to owner about who is aiming at them.&lt;br /&gt;
|-&lt;br /&gt;
||[[AntiDelay Node]]&lt;br /&gt;
||[[User:Xaviar Czervik|Xaviar Czervik]]&lt;br /&gt;
||Uses [[llMessageLinked]] to stop those pesky delays.&lt;br /&gt;
|-&lt;br /&gt;
||[[AO Overriding Pose Ball]]&lt;br /&gt;
||[[User:Jesse Barnett|Jesse Barnett]]&lt;br /&gt;
||No more turning your AO off and on when you sit down&lt;br /&gt;
|-&lt;br /&gt;
||[[ARCFOUR Strong Encryption Implementation]]&lt;br /&gt;
||[[User:Nekow42 Zarf|Nekow42 Zarf]]&lt;br /&gt;
||An LSL implementation of ARCFOUR, the most popular stream cipher still in use. It is licensed under a Creative Commons Attribution 3.0 license.&lt;br /&gt;
|-&lt;br /&gt;
||[[Assembly Programming Language|Assembly-Like Programming Language]]&lt;br /&gt;
||[[User:Xaviar Czervik|Xaviar Czervik]]&lt;br /&gt;
||A compiler that runs assembly-like programs.&lt;br /&gt;
|-&lt;br /&gt;
||[[Associative Array Emulator|Associative Array (Dictionary) Emulator]]&lt;br /&gt;
||[[User:Alynna Vixen|Alynna Vixen]]&lt;br /&gt;
||This library provides a set of functions for using a list as an associative array where string based keys can refer to one or more variant elements.&lt;br /&gt;
|-&lt;br /&gt;
||[[AvatarFollower]]&lt;br /&gt;
||[[User:Dale Innis|Dale Innis]]&lt;br /&gt;
||Allows one avatar to automatically follow another.&lt;br /&gt;
|-&lt;br /&gt;
||[[Avatar Radar (NewAge)]]&lt;br /&gt;
||[[User:Asia Snowfall|Asia Snowfall]]&lt;br /&gt;
||Version 1.2; A nice new avatar radar script i just finish that you can place in your hud, Also features avatar locator&lt;br /&gt;
|-&lt;br /&gt;
||[[Base2Dec]]&lt;br /&gt;
||[[User:Siann Beck|Siann Beck]]&lt;br /&gt;
||Convert a number to decimal from any base.&lt;br /&gt;
|-&lt;br /&gt;
||[[BaseN]]&lt;br /&gt;
||[[User:Nexii Malthus|Nexii Malthus]]&lt;br /&gt;
||Variable base compression, dynamically maps to usable UTF code points.&lt;br /&gt;
|-&lt;br /&gt;
||[[Basic A-Star Pathfinder]]&lt;br /&gt;
||[[User:Nexii Malthus|Nexii Malthus]]&lt;br /&gt;
|| My own interpretation of A Star into a highly efficient algorithmn&lt;br /&gt;
|-&lt;br /&gt;
||[[Basic Encryption Modules]]&lt;br /&gt;
||[[User:Beverly Larkin|Beverly Larkin]]&lt;br /&gt;
||Basic encryption scripts, allows you to encrypt a float and shout it to another prim on a randomly chosen channel.&lt;br /&gt;
|-&lt;br /&gt;
||[[Be happy]]&lt;br /&gt;
||[[User:Emmas Seetan|Emmas Seetan]]&lt;br /&gt;
||Basic smile attachment script, makes your avatar smile.&lt;br /&gt;
|-&lt;br /&gt;
||[[Best Neighbor Ad Hiding Script|Best Neighbor]]&lt;br /&gt;
||[[User:Doran Zemlja|Doran Zemlja]]&lt;br /&gt;
||Reduce ad clutter by hiding ads when users are on their own land nearby.&lt;br /&gt;
|-&lt;br /&gt;
||[[BigNum|BigNum Library (RSA Encryption)]]&lt;br /&gt;
||[[User:Xaviar Czervik|Xaviar Czervik]]&lt;br /&gt;
||A BigNum Library for dealing with big numbers! Specialized for modular multiplication, and RSA encryption.&lt;br /&gt;
|-&lt;br /&gt;
||[[Binary Clock v1.1|Binary Clock]]&lt;br /&gt;
||[[User:Fox Diller|Fox Diller]]&lt;br /&gt;
||A Binary Clock.&lt;br /&gt;
|-&lt;br /&gt;
||[[BinaryDecimalConverter]]&lt;br /&gt;
||[[User:Soundless Smalls|Soundless Smalls]]&lt;br /&gt;
||Converts a binary value to a decimal value and vice versa.&lt;br /&gt;
|-&lt;br /&gt;
||[[Blacklist and Remote Kill|Blacklist and Remote Kill]]&lt;br /&gt;
||[[User:Chase Quinnell|Chase Quinnell]]&lt;br /&gt;
||Blacklist(denial of use) or a creator kill script(can delete someone&#039;s item by command on private channel)&lt;br /&gt;
|-&lt;br /&gt;
||[[User_talk:Rolig_Loon/Bookmark_URLs|Bookmark URLs]]&lt;br /&gt;
||[[User:Rolig Loon|Rolig Loon]]&lt;br /&gt;
||Dialog driven HUD reads bookmarked URLs from notecards and navigates directly to them with user&#039;s in-world browser.&lt;br /&gt;
|-&lt;br /&gt;
||[[Builders Buddy|Builder&#039;s Buddy Tool]]&lt;br /&gt;
||[[User:Newfie Pendragon|Newfie Pendragon]]&lt;br /&gt;
||Script to easily move/rotate large builds that exceed the linkable size limit (30 meters).&lt;br /&gt;
|-&lt;br /&gt;
||[[Button Click Detector]]&lt;br /&gt;
||{{User|Sendao Goodman}}&lt;br /&gt;
||Use [[llDetectedTouchUV]] to determine which button was pressed on a texture.&lt;br /&gt;
|-&lt;br /&gt;
||[[Camera following prim]]&lt;br /&gt;
||[[User:Dale Innis|Dale Innis]]&lt;br /&gt;
||Pair of scripts to make a prim follow your camera position around (for lights etc).&lt;br /&gt;
|-&lt;br /&gt;
||[[Camera Sync]]&lt;br /&gt;
||[[User:Meyermagic Salome|Meyermagic Salome]] and [[User:Nomad Padar|Nomad Padar]]&lt;br /&gt;
||A system to synchronize the cameras of two avatars.&lt;br /&gt;
|-&lt;br /&gt;
||[[Chatbot]]&lt;br /&gt;
||Anonymous&lt;br /&gt;
||Compile and run the LSL you type on a channel, faster than you can thru the 2007-08 SL GUI.&lt;br /&gt;
|-&lt;br /&gt;
||[[Chat Logger (GPL)]]&lt;br /&gt;
||[[User:Nobody Fugazi|Nobody Fugazi]]&lt;br /&gt;
||Chat logger which requests permission from participants before recording them.&lt;br /&gt;
|-&lt;br /&gt;
||[[Chat_Relay|Chat Relay]]&lt;br /&gt;
||[[User:grumble Loudon|grumble Loudon]]&lt;br /&gt;
||A Chat relay which can be routed using a path header and won&#039;t echo.&lt;br /&gt;
|-&lt;br /&gt;
||[[ClickAndDrag]]&lt;br /&gt;
||[[User:Nexii Malthus|Nexii Malthus]]&lt;br /&gt;
|| Click and Drag user interface elements using dynamic feedback&lt;br /&gt;
|-&lt;br /&gt;
||[[Code Racer]]&lt;br /&gt;
||Anonymous&lt;br /&gt;
||Race two versions of code forever, to see which runs faster.&lt;br /&gt;
|-&lt;br /&gt;
||[[Code Sizer]]&lt;br /&gt;
||Anonymous&lt;br /&gt;
||Count the bytes compiled from source code, to measure how to write small code.&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Void_Singer/Programs#v7-D_Enh._Color_Picker|Color Changer]]&lt;br /&gt;
||[[User:Void_Singer|Void_Singer]]&lt;br /&gt;
||Dialog driven color changer. Supports 16million+ colors, web color codes, multiple targeted prims, with save and recall.&lt;br /&gt;
|-&lt;br /&gt;
||[[Color Changer|Color Changer Plus]]&lt;br /&gt;
||[[User:Neo Calcutt|Neo Calcutt]]&lt;br /&gt;
||A color changer with 14 colors, a random function, and a custom vector function.&lt;br /&gt;
|-&lt;br /&gt;
||[[Color conversion scripts|Color Conversion]]&lt;br /&gt;
||[[User:Sally LaSalle|Sally LaSalle]]&lt;br /&gt;
||Convert between Red Green Blue (RGB) and Hue Saturation Value (HSV).&lt;br /&gt;
|-&lt;br /&gt;
||[[ColorConvert]]&lt;br /&gt;
||[[User:Siann Beck|Siann Beck]]&lt;br /&gt;
||Convert color values to vector from RGB, hex or HTML color name.&lt;br /&gt;
|-&lt;br /&gt;
||[[Color script]]&lt;br /&gt;
||[[User:Masakazu Kojima|Masakazu Kojima]]&lt;br /&gt;
||Script for changing colors trough a listener with pre-defined colors.&lt;br /&gt;
|-&lt;br /&gt;
||[[Library Combined Library|Combined Library]]&lt;br /&gt;
||[[User:Strife Onizuka|Strife Onizuka]]&lt;br /&gt;
||Library of mostly encoding and decoding functions, some more useful then others.&lt;br /&gt;
* String functions: Replace / Trim right / Trim left / Trim both&lt;br /&gt;
* Unicode conversion: UTF8 to Unicode / Unicode to UTF8&lt;br /&gt;
* List functions: Replace / Compare&lt;br /&gt;
|-&lt;br /&gt;
||[[Computer:jaycoonlanguage]]&lt;br /&gt;
||[[User:jayco121 Bing|jayco121 Bing]]&lt;br /&gt;
|| A language written in LSL that is meant for my computer (available at the shop).&lt;br /&gt;
|-&lt;br /&gt;
||[[Read Note Card Configuration|Configuration Notecard Reader]]&lt;br /&gt;
||{{User|Dedric Mauriac}}&lt;br /&gt;
||A script to read configuration information from a notecard. Parses notecard to extract key words and their assigned values. Allows for comment lines and many more useful features.&lt;br /&gt;
|-&lt;br /&gt;
||[[AdvancedNotecardReader|Configuration Notecard Reader (advanced)]]&lt;br /&gt;
||[[Lear Cale|Lear Cale]]&lt;br /&gt;
||Robust configuration notecard reader; supports multiple notecards with same suffix, handles reconfig on inventory change, and does not usurp the default state.&lt;br /&gt;
|-&lt;br /&gt;
||[[Library_Chat_Relay|Conversation Relay]]&lt;br /&gt;
||[[User:Jippen Faddoul|Jippen Faddoul]]&lt;br /&gt;
||Chat relay which requests permission from participants before relaying their messages. Also includes their attachments. (ToS compliant).&lt;br /&gt;
|-&lt;br /&gt;
||[[Curtain script]]&lt;br /&gt;
||[[User:Zilla Larsson|Zilla Larsson]]&lt;br /&gt;
||A simple script to retract/stretch curtains, blinds, bedcovers and more&lt;br /&gt;
|-&lt;br /&gt;
||[[Dataserver API]]&lt;br /&gt;
||[[User:Revolution Perenti|Revolution Perenti]]&lt;br /&gt;
||Dataserver Framework for Notecards.&lt;br /&gt;
|-&lt;br /&gt;
||[[Date Library]]&lt;br /&gt;
||[[User:Corto Maltese|Corto Maltese]]&lt;br /&gt;
|| Date library, based on number of day since march 3rd 1600, can be used to calculate weekday, date differences, and date offset, and date formating.&lt;br /&gt;
|-&lt;br /&gt;
||[[Day of the Week]]&lt;br /&gt;
||[[User:DoteDote Edison|DoteDote Edison]]&lt;br /&gt;
||Function to get day of the week from [[llGetUnixTime]].&lt;br /&gt;
|-&lt;br /&gt;
||[[Days in Month]]&lt;br /&gt;
||[[User:IntLibber Brautigan begin_of_the_skype_highlighting     end_of_the_skype_highlighting begin_of_the_skype_highlighting     end_of_the_skype_highlighting|IntLibber Brautigan]]&lt;br /&gt;
||Clicking on it returns the number of days in the present month. Useful for scripting calendars and tier systems that need to know the number of days in the month at hand or to calculate for any month. Even adjusts for leap years.&lt;br /&gt;
|-&lt;br /&gt;
||[[Deed Tools]]&lt;br /&gt;
||[[User:Falados Kapuskas|Falados Kapuskas]]&lt;br /&gt;
||Tools that allow the creator to modify Group-Owned (Deeded) Objects via chat.&lt;br /&gt;
|-&lt;br /&gt;
||[[Describe Chatter]]&lt;br /&gt;
||Anonymous&lt;br /&gt;
||Chat to see yourself as others do.&lt;br /&gt;
|-&lt;br /&gt;
||[[Dialog Control]]&lt;br /&gt;
||[[User:Nargus Asturias|Nargus Asturias]]&lt;br /&gt;
|| A (not-so) simple dialog &amp;amp; menu control script. Call dialog and receive selected value via [[link_message]](), with built-in timer and [[link_message]]() notification on time out. Supports multi-pages dialog and numeric property dialog. Button text and dialog&#039;s returned value can differ.&lt;br /&gt;
Latest version also has [[Dialog Menus Control]] built-in; which allow multi-level menus through SL dialog system.&lt;br /&gt;
|-&lt;br /&gt;
||[[MultiUser_Dialog_Handler|Dialog Menus (multiuser)]]&lt;br /&gt;
||[[User:SimonT Quinnell|SimonT Quinnell]]&lt;br /&gt;
|| Menu dialog handler that supports multiple menus open at once from the single script. Displays multi-page menus if necessary as well as allowing for fixed header and footer buttons.  Timeouts as well as Text and button size limits are handled.&lt;br /&gt;
|-&lt;br /&gt;
||[[Dialog NumberPad|Dialog Number Pad]]&lt;br /&gt;
||[[User:DoteDote Edison|DoteDote Edison]]&lt;br /&gt;
||Use a dialog to accept positive integer input from users.&lt;br /&gt;
|-&lt;br /&gt;
||[[Displayer Script]]&lt;br /&gt;
||[[User:Akinori Kimagawa|Akinori Kimagawa]]&lt;br /&gt;
||[[Display Words On Top Of An Object]]&lt;br /&gt;
|-&lt;br /&gt;
||[[Drink script]]&lt;br /&gt;
||[[User:Emmas Seetan|Emmas Seetan]]&lt;br /&gt;
||Used mainly for food and drink in Second Life.&lt;br /&gt;
|-&lt;br /&gt;
||[[TOXDropBox|DropBox]]&lt;br /&gt;
||[[User:Dimentox Travanti|Dimentox Travanti]]&lt;br /&gt;
|| This is a Drop box which allows people to drop certain items in a object &amp;amp; has many config options.&lt;br /&gt;
|-&lt;br /&gt;
||[[Efficiency Tester]]&lt;br /&gt;
||[[User:Xaviar Czervik|Xaviar Czervik]]&lt;br /&gt;
||Tests the speed of a function.&lt;br /&gt;
|-&lt;br /&gt;
||[[Email-to-IM|Email2IM]]&lt;br /&gt;
||[[User:DoteDote Edison|DoteDote Edison]]&lt;br /&gt;
||Send IMs to SL friends via [[email]] (translate emails from friends into IMs).&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Kireji_Haiku/SIMchat_headset|Encrypted Region-wide chat]]&lt;br /&gt;
||&#039;&#039;&#039;[[User:Kireji Haiku|Kireji Haiku]]&#039;&#039;&#039; &amp;lt;sup&amp;gt;&amp;lt;small&amp;gt;([[User talk:Kireji Haiku|talk]]|[[Special:Contributions/Kireji Haiku|contribs]])&amp;lt;/small&amp;gt;&amp;lt;/sup&amp;gt;&lt;br /&gt;
||Encrypted Region-wide chat&lt;br /&gt;
|-&lt;br /&gt;
||[[ExplodingObjects]]&lt;br /&gt;
||[[User:Dale Innis|Dale Innis]]&lt;br /&gt;
||Causes an object (of the appropriate type) to explode on command.&lt;br /&gt;
|-&lt;br /&gt;
||[[FadeEasy]]&lt;br /&gt;
||[[User:Nika Rugani|Nika Rugani]]&lt;br /&gt;
||The easy way of fading objects in or out (Using llSetLinkAlpha)&lt;br /&gt;
|-&lt;br /&gt;
||[[FastConeSpread]]&lt;br /&gt;
||[[User:Nexii Malthus|Nexii Malthus]]&lt;br /&gt;
|| Fast Algorithmn to achieve cone spread, main use in weaponry systems.&lt;br /&gt;
|-&lt;br /&gt;
||[[User:PixelProphet Lane/Scripts#Fast List Prim Contents|Fast List Prim Contents]]&lt;br /&gt;
||[[User:PixelProphet Lane|PixelProphet Lane]]&lt;br /&gt;
||Fast and efficient method to print Object Inventory (Name, Type and next Owner permissions)&lt;br /&gt;
|-&lt;br /&gt;
||[[Find Avatar Key|Find Avatar Key]]&lt;br /&gt;
||[[User:Huney Jewell|Huney Jewell]]&lt;br /&gt;
||Explores [[UUID]] of avatar whose name is said in local chat or who touches the prim.&lt;br /&gt;
|-&lt;br /&gt;
||[[First Name Letter Prize]]&lt;br /&gt;
||[[User:RaithSphere Whybrow|RaithSphere Whybrow]]&lt;br /&gt;
||Gives a prize if the person who sits on it&#039;s first letter of first name matches the random letter!&lt;br /&gt;
|-&lt;br /&gt;
||[[Fix Small Prims|Fix_Small_Prims]]&lt;br /&gt;
||[[User:Emma Nowhere|Emma Nowhere]]&lt;br /&gt;
||Finds and adjusts the smallest prims in a linkset so that it can be scaled down further.&lt;br /&gt;
|-&lt;br /&gt;
||[[Float2Hex]]&lt;br /&gt;
||[[User:Strife Onizuka|Strife Onizuka]]&lt;br /&gt;
||Very useful for transporting [[float|floats]].&lt;br /&gt;
|-&lt;br /&gt;
||[[Float Box Contents]]&lt;br /&gt;
||[[User:Rolig Loon|Rolig Loon]]&lt;br /&gt;
||Displays object inventory in hover text, identified by type and updated marquee-style.&lt;br /&gt;
|-&lt;br /&gt;
||[[Follower (script)|Follower]]&lt;br /&gt;
||Unknown, uploaded by [[User:Slik Swindlehurst|Slik Swindlehurst]]&lt;br /&gt;
||Makes an object follow the nearest person. Do not use for [[grief|griefing]].&lt;br /&gt;
|-&lt;br /&gt;
||[[GA Event Notifier]]&lt;br /&gt;
||[[User:Victor Hua|Victor Hua]]&lt;br /&gt;
||Gathers seven days event data from a Google calendar and display it through a simple interface. The lsl script can access several calendars at once through seperate php files. One file per calendar. Host the included php on your own web server.&lt;br /&gt;
|-&lt;br /&gt;
||[[Geometric|Geometric Library]]&lt;br /&gt;
||Community Project&lt;br /&gt;
||A substantial amount of various geometric functions for intersection and other purposes of 3D maths.&lt;br /&gt;
|-&lt;br /&gt;
||[[Get Profile Picture]]&lt;br /&gt;
||[[User:Valentine Foxdale|Valentine Foxdale]]&lt;br /&gt;
||Sets the texture of the object to profile picture of the person that touches ot&lt;br /&gt;
|-&lt;br /&gt;
||[[GetTimestampOffset]]&lt;br /&gt;
||[[User:Siann_Beck|Siann Beck]]&lt;br /&gt;
||Returns [[llGetTimestamp]]() with an hour offset.&lt;br /&gt;
|-&lt;br /&gt;
||[[Give InvItem every n hours]]&lt;br /&gt;
||[[User:Criz Collins|Criz Collins]]&lt;br /&gt;
||Will give an inventory item on touch only every n hours, even if somebody touches the object more than once. &lt;br /&gt;
|-&lt;br /&gt;
||[[Give random object]]&lt;br /&gt;
||[[User:Emmas Seetan|Emmas Seetan]]&lt;br /&gt;
||Touch to receive a random object in the prim&#039;s inventory&lt;br /&gt;
|-&lt;br /&gt;
||[[Google Charts]]&lt;br /&gt;
||[[User:Dedric Mauriac|Dedric Mauriac]]&lt;br /&gt;
||Create links to display raw data as a chart image.&lt;br /&gt;
|-&lt;br /&gt;
||[[Google_Translator]]&lt;br /&gt;
||[[User:Ugleh Ulrik|Ugleh Ulrik]]&lt;br /&gt;
||Translates spanish to english, and its simple to make it any other way.&lt;br /&gt;
|-&lt;br /&gt;
||[[Go transparent when walking]]&lt;br /&gt;
||[[User:Emmas Seetan|Emmas Seetan]]&lt;br /&gt;
||An attachment that goes invisible when you walk and visible when you don&#039;t walk.&lt;br /&gt;
|-&lt;br /&gt;
||[[Group Authorization]]&lt;br /&gt;
||[[User:Chase Quinnell|Chase Quinnell]]&lt;br /&gt;
||Checks to see if object is set to appropriate group (checks by [[Group key finder|group key]])&lt;br /&gt;
|-&lt;br /&gt;
||[[Group Information v1.0]]&lt;br /&gt;
||[[User:Tyrennic Rivera|Tyrennic Rivera]]&lt;br /&gt;
||When clicked the prim will show group information (set on the prim) from the official Second Life Search page.&lt;br /&gt;
|-&lt;br /&gt;
||[[Group key finder]]&lt;br /&gt;
||[[User:Emmas Seetan|Emmas Seetan]]&lt;br /&gt;
||Touch to find the key of the group&lt;br /&gt;
|-&lt;br /&gt;
||[[Group Privacy]]&lt;br /&gt;
||[[User:Chance Unknown|Chance Unknown]]&lt;br /&gt;
||Security device to insure members of a group can have a private area. Deactivates when nobody present.&lt;br /&gt;
|-&lt;br /&gt;
||[[Hello Avatar]]&lt;br /&gt;
||Linden Lab&lt;br /&gt;
||SL&#039;s default script.&lt;br /&gt;
|-&lt;br /&gt;
||[[Hello Avatar Companion]]&lt;br /&gt;
||[[Chase Quinnell]]&lt;br /&gt;
||Companion to the original [[Hello Avatar]] script&lt;br /&gt;
|-&lt;br /&gt;
||[[Hierarchics]]&lt;br /&gt;
||[[User:Nexii Malthus|Nexii Malthus]]&lt;br /&gt;
|| Advanced script to create an efficient self-aware hierarchic structure.&lt;br /&gt;
|-&lt;br /&gt;
||[[Hierarchics2]]&lt;br /&gt;
||[[User:Nexii Malthus|Nexii Malthus]]&lt;br /&gt;
|| Second generation, one script per object. Only two functions, can be embedded in others. Uses the new [[llSetLinkPrimitiveParamsFast]]/[[llGetLinkPrimitiveParams]] functions.&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Rolig_Loon/High-Capacity_Greeter-Counter|High-Capacity Greeter-Counter]]&lt;br /&gt;
||[[User:Rolig Loon|Rolig Loon]]&lt;br /&gt;
||Uses a memory compression algorithm to store hashed visitor UUID&#039;s in a string instead of using a list. &lt;br /&gt;
|-&lt;br /&gt;
||[[Holodeck]]&lt;br /&gt;
||[[User:Revolution Perenti|Revolution Perenti]]&lt;br /&gt;
||Home Rezzing System (Open Source).&lt;br /&gt;
|-&lt;br /&gt;
||[[Interpolation|Interpolation Library]]&lt;br /&gt;
||[[User:Nexii_Malthus|Nexii Malthus]]&lt;br /&gt;
||A small set of interpolation functions.&lt;br /&gt;
|-&lt;br /&gt;
||[[Intra-Region Update Server]]&lt;br /&gt;
||[[User:Emma_Nowhere|Emma Nowhere]]&lt;br /&gt;
||Centrally update objects such as Freeview screens or teleport pads within a region that are configured by notecards or contain modifiable objects or media assets.&lt;br /&gt;
|-&lt;br /&gt;
||[[Inventory_Based_Menu]]&lt;br /&gt;
||[[User:Revolution Perenti|Revolution Perenti]]&lt;br /&gt;
||Inventory Based Menu System.&lt;br /&gt;
|-&lt;br /&gt;
||[[iTunes RPC Email|iTunes RPC]]&lt;br /&gt;
||[[User:Fox Diller|Fox Diller]]&lt;br /&gt;
||iTunes RPC via LSL [[llEmail]] and [[llRemoteDataReply]].&lt;br /&gt;
|-&lt;br /&gt;
||[[Kilt Editor|Kilt / Skirt Editor]]&lt;br /&gt;
||[[User:Brangus Weir|Brangus Weir]]&lt;br /&gt;
|| This changes all the parameters of all the flexis in the link set in one swell foop!&lt;br /&gt;
|-&lt;br /&gt;
||[[Key Pad Door|Keypad Door]]&lt;br /&gt;
||[[User:Tdub Dowler|Tdub Dowler]]&lt;br /&gt;
|| Door and keypad with changeable code. Follow instructions carefully!&lt;br /&gt;
|-&lt;br /&gt;
||[[Last Sound System]]&lt;br /&gt;
||[[User:Babbage Linden|Babbage Linden]]&lt;br /&gt;
||An LSL [http://Last.fm Last.fm] client.&lt;br /&gt;
|-&lt;br /&gt;
||[[LibraryDisplayLandScreenshot]]&lt;br /&gt;
||[[User:Jon Desmoulins|Jon Desmoulins]]&lt;br /&gt;
||A modified version of LibraryDisplayProfilePic using the new [[PARCEL_DETAILS_ID]] Implemented in Server v1.36&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Daemonika_Nightfire/Scripts/LinkNumber-List_in_llSetLinkPrimitiveParamsFast|LinkNumber-List_in_llSetLinkPrimitiveParamsFast]]&lt;br /&gt;
||[[User:Daemonika Nightfire|Daemonika Nightfire]]&lt;br /&gt;
||Changed several prims in linkset with the same parameters, with a list. Without separate llSetLinkPrimitiveParams for each prim. (ideal for show/hide effects)&lt;br /&gt;
|-&lt;br /&gt;
||[[Linkset resizer]]&lt;br /&gt;
||[[User:Maestro Linden|Maestro Linden]]&lt;br /&gt;
||Systematically rescales a linkset by moving and resizing each prim (by using [[llGetLinkPrimitiveParams]] and [[llSetLinkPrimitiveParamsFast]])&lt;br /&gt;
|-&lt;br /&gt;
||[[Linkset resizer with menu]]&lt;br /&gt;
||[[User:Brilliant Scientist|Brilliant Scientist]]&lt;br /&gt;
||A menu-driven script that rescales a linkset by moving and resizing the prims using [[llGetLinkPrimitiveParams]] and [[llSetLinkPrimitiveParamsFast]] functions. Based on [[Linkset resizer]].&lt;br /&gt;
|-&lt;br /&gt;
||[[Linkset Resizer 2]]&lt;br /&gt;
||[[User:Emma Nowhere|Emma Nowhere]]&lt;br /&gt;
||A more user-friendly resizer script designed for either drop-in use by the end-user or builder or for use in products. Based on [[Fix Small Prims]].&lt;br /&gt;
|-&lt;br /&gt;
||[[List2CSV]]&lt;br /&gt;
||[[User:Kunnis Basiat|Kunnis Basiat]]&lt;br /&gt;
||List2CSV &amp;amp; CSV2List that include preserving type and escaping characters.&lt;br /&gt;
|-&lt;br /&gt;
||[[list_cast]]&lt;br /&gt;
||[[User:Fractured Crystal|Fractured Crystal]]&lt;br /&gt;
||Casts a list of strings into the type they appear to be. Designed for preprocessing user input for feeding into [[llSetPrimitiveParams]]&lt;br /&gt;
|-&lt;br /&gt;
||[[Listener Script]]&lt;br /&gt;
||[[User:Emmas Seetan|Emmas Seetan]]&lt;br /&gt;
||Use to [[listen]] to other people&#039;s conversations (Like spying)&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Void_Singer/Functions#List:_Find_Last_Index|List: Find Last Index]]&lt;br /&gt;
||[[User:Void Singer|Void Singer]]&lt;br /&gt;
||Returns the last index of searched item in a source list. (backwards version of [[llListFindList]])&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Void_Singer/Functions#List:_Multi-Find_Index_.28First_or_Last.29|List: Multi Find]]&lt;br /&gt;
||[[User:Void Singer|Void Singer]]&lt;br /&gt;
||Returns the first found index of multiple search items in a list. (Multi-item version of [[llListFindList]]. Fwd and Rev versions included)&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Void_Singer/Functions#List:_Get_Reverse_Order|List: Reverse]]&lt;br /&gt;
||[[User:Void Singer|Void Singer]]&lt;br /&gt;
||Returns an input List in reverse order&lt;br /&gt;
|-&lt;br /&gt;
||[[Load URL]]&lt;br /&gt;
||[[User:Emmas Seetan|Emmas Seetan]]&lt;br /&gt;
||Touch to get a dialog to visit the URL inside the script.&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Kephra_Nurmi/lsDancemachine|lsDancemachine]]&lt;br /&gt;
||[[User:Kephra_Nurmi|Kephra Nurmi]]&lt;br /&gt;
|| Low lag client server dancemachine.&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Kephra_Nurmi/lsDeejay|lsDeejay Home Edition]] &lt;br /&gt;
||[[User:Kephra_Nurmi|Kephra Nurmi]]&lt;br /&gt;
|| Client server media control for music, video, youtube, pictures, and texture animations.&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Kephra_Nurmi/lsDialog|lsDialog]]&lt;br /&gt;
||[[User:Kephra_Nurmi|Kephra Nurmi]]&lt;br /&gt;
|| Universal notecard driven menu dialog system.&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Kephra_Nurmi/lsDisplay|lsDisplay]]&lt;br /&gt;
||[[User:Kephra_Nurmi|Kephra Nurmi]]&lt;br /&gt;
|| Picture cycler with preloader.&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Kephra_Nurmi/lsDistributor|lsDistributor]]&lt;br /&gt;
||[[User:Kephra_Nurmi|Kephra Nurmi]]&lt;br /&gt;
|| Simple &#039;try&#039; and &#039;buy&#039; vendor system.&lt;br /&gt;
|-&lt;br /&gt;
||[[LSL_languageAPI]]&lt;br /&gt;
||[[User:Gypsy paz|Gypsy Paz]]&lt;br /&gt;
||Multi-lingual API from notecard based language files&lt;br /&gt;
|-&lt;br /&gt;
||[[Mandelbrot Explorer]]&lt;br /&gt;
||[[User:Babbage Linden|Babbage Linden]]&lt;br /&gt;
||An interactive fractal explorer.&lt;br /&gt;
|-&lt;br /&gt;
||[[Merge Sort]]&lt;br /&gt;
||[[User:Xaviar Czervik|Xaviar Czervik]]&lt;br /&gt;
||Implements a Merge Sort in LSL, however this code is 300(ish) times slower than [[llListSort]]. Don&#039;t use this in a script!&lt;br /&gt;
|-&lt;br /&gt;
||[[Minesweeper]]&lt;br /&gt;
||[[User:Xaviar Czervik|Xaviar Czervik]]&lt;br /&gt;
||A simple minesweeper game.&lt;br /&gt;
|-&lt;br /&gt;
||[[Morse Code]]&lt;br /&gt;
||{{User|Bobbyb30 Swashbuckler}}&lt;br /&gt;
||A script that allows for the conversion to and from morse code and can &amp;quot;play&amp;quot; morse code.&lt;br /&gt;
|-&lt;br /&gt;
||[[Multi Item Rezzer|Multi Item Rezzer]]&lt;br /&gt;
||[[User:Jesse Barnett|Jesse Barnett]]&lt;br /&gt;
||A rework of my old High Altitude Rezzer. Place the objects that you wish to choose from to be rezzed inside. Sit on the rezzer and pick the item and the height. It will go to target height and rez the object.&lt;br /&gt;
|-&lt;br /&gt;
||[[Multirezzer|Multirezzer (on collision)]]&lt;br /&gt;
||[[User:Beet Streeter|Beet Streeter]]&lt;br /&gt;
||Spawns up to 10 objects when the object containing the script collides with a user.&lt;br /&gt;
|-&lt;br /&gt;
||[[Name2Key in LSL]]&lt;br /&gt;
||[[User:Maeva Anatine|Maeva Anatine]]&lt;br /&gt;
||Get the Name2Key feature inside your scripts. Works even on lastly subscribed avatars.&lt;br /&gt;
|-&lt;br /&gt;
||[[No Auto-Return]]&lt;br /&gt;
||[[User:Bellla Clarity|Bella Clarity]]&lt;br /&gt;
||To stop your long and hard builds from getting returned in sandboxes (&#039;&#039;only single prims, though!&#039;&#039;).&lt;br /&gt;
|-&lt;br /&gt;
||[[No Auto-Return NR]]&lt;br /&gt;
||[[User:Jor3l Boa|Jor3l Boa]]&lt;br /&gt;
||This really works (29-05-09), needs a nearby region to do the trick and avoid auto return. (Tested on Blue and Rausch)&lt;br /&gt;
|-&lt;br /&gt;
||[[No Auto-Return (Multi)]]&lt;br /&gt;
||[[User:Xaviar Czervik|Xaviar Czervik]]&lt;br /&gt;
||A revision of Bella&#039;s that works for multi-prim objects.&lt;br /&gt;
|-&lt;br /&gt;
||[[No Limit Teleporter]]&lt;br /&gt;
||[[User:Morgam Biedermann|Morgam Biedermann]]&lt;br /&gt;
||Teleport to infinite altitudes (up to 4096m)&lt;br /&gt;
|-&lt;br /&gt;
||[[Object Size]]&lt;br /&gt;
||[[User:Chase Quinnell|Chase Quinnell]]&lt;br /&gt;
||Gets the dimensions and footprint of a linkset&lt;br /&gt;
|-&lt;br /&gt;
||[[Object to Data v1.3|Object to Data]]&lt;br /&gt;
||[[User:Xaviar Czervik|Xaviar Czervik]]&lt;br /&gt;
||Turns an object into text (and back). Allows people to transfer objects through notecards (or otherwise).&lt;br /&gt;
|-&lt;br /&gt;
||[[Online Indicator|Online Indicator]]&lt;br /&gt;
||[[User:Kristy Fanshaw|Kristy Fanshaw]]&lt;br /&gt;
||Will show if the user is online or not. Displays users profile picture and allows to send IM&#039;s to user. Also gives a link to open the users profile&lt;br /&gt;
|-&lt;br /&gt;
||[[Open Group Join]]&lt;br /&gt;
||[[User:Alicia Stella|Alicia Stella]]&lt;br /&gt;
||User Touches Object to Join Group from Group Info window, (no bot.)&lt;br /&gt;
|-&lt;br /&gt;
||[[Open Zyngo Skin Installer]]&lt;br /&gt;
||[[User:Tmzasz Luminos|Tmzasz Luminos]]&lt;br /&gt;
||A Simple Script designed to install skins on the popular Skill machines.&lt;br /&gt;
|-&lt;br /&gt;
||[[One Script, many doors]]&lt;br /&gt;
||[[User:Kyrah Abattoir|Kyrah Abattoir]]&lt;br /&gt;
||Door script able to manage more than 50 linked doors from a single script instance.&lt;br /&gt;
|-&lt;br /&gt;
||[[ParseString2List]]&lt;br /&gt;
||[[User:Strife Onizuka|Strife Onizuka]]&lt;br /&gt;
||Same as [[llParseString2List]] and [[llParseStringKeepNulls]], but not limited to 8 spacers or separators. Thus substitute a call to the [[llParseString2List]] and [[llParseStringKeepNulls]] functions by a call to [[Parse_String_To_List|ParseString2List]] whenever you have more than 8 separators or more than 8 spacers.&lt;br /&gt;
|-&lt;br /&gt;
||[[Password Generator]]&lt;br /&gt;
||[[User:Syntrax Canucci|Syntrax Canucci]]&lt;br /&gt;
||This is an over-complicated, semi-complex password generator, which goes through multiple steps.&lt;br /&gt;
|-&lt;br /&gt;
||[[Pathfinder]]&lt;br /&gt;
||[[User:Babbage Linden|Babbage Linden]]&lt;br /&gt;
||A potential field based pathfinding library.&lt;br /&gt;
|-&lt;br /&gt;
||[[Phantom Child]]&lt;br /&gt;
||[[User:Aeron Kohime|Aeron Kohime]]&lt;br /&gt;
||Causes a child in a link set to become phantom without the entire object becoming phantom.&lt;br /&gt;
|-&lt;br /&gt;
||[[PhysicsLib]]&lt;br /&gt;
||[[User:Nexii Malthus|Nexii Malthus]]&lt;br /&gt;
|| Interesting set of fun physics functions.&lt;br /&gt;
|-&lt;br /&gt;
||[[Play and Loop Sound]]&lt;br /&gt;
||[[User:Bellla Clarity|Bella Clarity]]&lt;br /&gt;
||Very short and simple script; plays and loops a sound in an object.&lt;br /&gt;
|-&lt;br /&gt;
||[[Posing stand|Posing Stand]]&lt;br /&gt;
||[[User:Bellla Clarity|Bella Clarity]]&lt;br /&gt;
||Just a pose script to edit [[attachments]] more easily.&lt;br /&gt;
|-&lt;br /&gt;
||[[PosJump]]&lt;br /&gt;
||[[User:Uchi Desmoulins|Uchi Desmoulins]]&lt;br /&gt;
||A much more efficient alternative to the popular [[warpPos]] function for bypassing 10m distance-moved limitations.&lt;br /&gt;
|-&lt;br /&gt;
||[[Prefix Calculator]]&lt;br /&gt;
||[[User:Xaviar Czervik|Xaviar Czervik]]&lt;br /&gt;
|| A calculator that evaluates expressions in prefix notation. &lt;br /&gt;
&amp;lt;code&amp;gt;+ 3 4 = 5. * + 1 2 + 3 4 = 14.&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||[[Progress Bar]]&lt;br /&gt;
||[[User:Nexii Malthus|Nexii Malthus]]&lt;br /&gt;
|| Flexible and powerful little function for creating progress bars useful in hovertext.&lt;br /&gt;
|-&lt;br /&gt;
||[[Pseudo-random Number Generator]]&lt;br /&gt;
||[[User:Xaviar Czervik|Xaviar Czervik]]&lt;br /&gt;
||Generates a Pseudo-random number between -0x7FFFFFFF and 0x7FFFFFFF&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Rolig_Loon/Quiz_From_Notecard|Quiz From Notecard]]&lt;br /&gt;
||[[User:Rolig Loon|Rolig Loon]]&lt;br /&gt;
||A multiple-choice testing script that reads questions and answer choices from a notecard and presents them in dialog boxes.&lt;br /&gt;
|-&lt;br /&gt;
||[[Rainbow_palette]]&lt;br /&gt;
||[[User:Rui Clary|Rui Clary]]&lt;br /&gt;
||Build a color picker, using only one prim, and a few lines of code.&lt;br /&gt;
|-&lt;br /&gt;
||[[Random AV Profile Projector]]&lt;br /&gt;
||[[User:Debbie Trilling|Debbie Trilling]]&lt;br /&gt;
||Randomly selects an AV from a crowd &amp;amp; then projects their profile picture as a &#039;holographic&#039; image &lt;br /&gt;
|-&lt;br /&gt;
||[[Random Object Vendor]]&lt;br /&gt;
||[[User:CodeBastard Redgrave|CodeBastard Redgrave]]&lt;br /&gt;
||Simple vendor that gives out random objects when paid the right amount &lt;br /&gt;
|-&lt;br /&gt;
||[[Random Password Generator]]&lt;br /&gt;
||[[User:Jor3l Boa|Jor3l Boa]]&lt;br /&gt;
||Generate Random passwords based on String Length.&lt;br /&gt;
|-&lt;br /&gt;
||[[RavText]]&lt;br /&gt;
||[[User:Ravenous Dingo|Ravenous Dingo]]&lt;br /&gt;
||An alternate to XyText.  This is a lightweight, multiple font 10 character text display system.  It only supports uppercase alphanumeric text and a few special characters, but it is very fast, renders quickly and supports multiple fonts. It is meant for specialized use when all that is desired is basic, fast text display and the extra &amp;quot;bells and whistles&amp;quot; are not needed.&lt;br /&gt;
|-&lt;br /&gt;
||[[User:PixelProphet Lane/Scripts#Real Object Inventory To Dialog|Real Object Inventory To Dialog]]&lt;br /&gt;
||[[User:PixelProphet Lane|PixelProphet Lane]]&lt;br /&gt;
||Display any amount of items contained in an Object in a Dialog, regardless of item name length.&lt;br /&gt;
|-&lt;br /&gt;
||[[RentalBoxv1|Rental Box, Simply]]&lt;br /&gt;
||[[User:Sendao Goodman|Sendao Goodman]]&lt;br /&gt;
||Simple explanation of how to make rental boxes.&lt;br /&gt;
|-&lt;br /&gt;
||[[Rental Cube]]&lt;br /&gt;
||[[User:Hank Ramos|Hank Ramos]]&lt;br /&gt;
||Simple cube for renting out a space.&lt;br /&gt;
|-&lt;br /&gt;
||[[Remote Texture Loader]]&lt;br /&gt;
||{{User|Bobbyb30 Swashbuckler}}&lt;br /&gt;
||A set of scripts for remotely loading textures within a sim. This means that a single &amp;quot;texture server&amp;quot; can manage multiple changing billboards within a sim.&lt;br /&gt;
|-&lt;br /&gt;
||[[Say Region Frames Per Second|Region Frames Per Second]]&lt;br /&gt;
||[[User:Heymeriou Mystakidou|Heymariou Mystakidou]]&lt;br /&gt;
|| Says the region name and frames per second out loud on command.&lt;br /&gt;
|-&lt;br /&gt;
||[[sbDialog]]&lt;br /&gt;
||[[User:Siann_Beck|Siann Beck]]&lt;br /&gt;
||A simple replacement function for [[llDialog]]. It re-orders the button list so that the button values, as passed to it, display left-to-right, top-to-bottom. It also opens a [[llListen|listen]] on the specified channel, and returns the handle.&lt;br /&gt;
|-&lt;br /&gt;
||[[Scheduler]]&lt;br /&gt;
||[[User:Haravikk Mistral|Haravikk Mistral]]&lt;br /&gt;
||Schedule multiple events using a single script timer&lt;br /&gt;
|-&lt;br /&gt;
||[[Scheme_Interpreter|Scheme Interpreter]]&lt;br /&gt;
||[[User:Xaviar Czervik|Xaviar Czervik]]&lt;br /&gt;
||A scheme interpreter capable of handling most scheme expressions, including lambda and lists.&lt;br /&gt;
|-&lt;br /&gt;
||[[Script Override Functions]]&lt;br /&gt;
||[[User:Revolution Perenti|Revolution Perenti]]&lt;br /&gt;
||bypass default strings, integer etc in chat channel.&lt;br /&gt;
|-&lt;br /&gt;
||[[Scripted Attachment Detector.lsl|Scripted Attachment Detector]]&lt;br /&gt;
||{{User|Bobbyb30 Swashbuckler}}&lt;br /&gt;
||A script that will display avatars wearing scripted attatchments in hovertext. This may be worn as an hud or rezzed. &lt;br /&gt;
|-&lt;br /&gt;
||[[Self Upgrading Script Enhanced]]&lt;br /&gt;
||[[User:Cron Stardust|Cron Stardust]]&lt;br /&gt;
||Keeps only latest version of the script on prim (even with multiple adds of the same script!)&lt;br /&gt;
|-&lt;br /&gt;
||[[Sensor Visualizer]]&lt;br /&gt;
||[[User:Cerise Sorbet|Cerise Sorbet]]&lt;br /&gt;
||Shows the size and shape you get with [[llSensor]] range and arc parameters&lt;br /&gt;
|-&lt;br /&gt;
||[[Serverless Key Exchange]]&lt;br /&gt;
||[[User:Sendao Goodman|Sendao Goodman]]&lt;br /&gt;
||Maintains a network of object keys without using an external server.&lt;br /&gt;
|-&lt;br /&gt;
||[[SHA1|SHA1 Hash]]&lt;br /&gt;
||[[User:Xaviar Czervik|Xaviar Czervik]]&lt;br /&gt;
||Preforms a SHA1 Hash on an input text. Similar to MD5 only (slightly) more secure. &lt;br /&gt;
|-&lt;br /&gt;
||[[Sim Map Particle Projector]]&lt;br /&gt;
||[[User:Jesse Barnett|Jesse Barnett]]&lt;br /&gt;
||Displays a floating map of the sim the script is in.&lt;br /&gt;
|-&lt;br /&gt;
||[[Sim Restart Logger]]&lt;br /&gt;
||[[User:Kyrah Abattoir|Kyrah Abattoir]]&lt;br /&gt;
||Counts region restarts and displays log of last 9 restarts together with region FPS and dilation. &lt;br /&gt;
|-&lt;br /&gt;
||[[Simple Elevator in a Box]]&lt;br /&gt;
||[[User:Hank Ramos|Hank Ramos]]&lt;br /&gt;
||Simple elevator example.&lt;br /&gt;
|-&lt;br /&gt;
||[[Skillingo AntiHack Script]]&lt;br /&gt;
||[[User:Tmzasz Luminos|Tmzasz Luminos]]&lt;br /&gt;
||A simple Protection script for skillingo thats modifyable to work with other machines.&lt;br /&gt;
|-&lt;br /&gt;
||[[Skunk Money]]&lt;br /&gt;
||[[User:Hank Ramos|Hank Ramos]]&lt;br /&gt;
||Fun gambling machine of yesteryear, which only supports freeplay now due to SL regulations against gambling. &lt;br /&gt;
|-&lt;br /&gt;
||[[SLateIt]]&lt;br /&gt;
||[[User:Babbage Linden|Babbage Linden]]&lt;br /&gt;
||An augmented virtual reality HUD.&lt;br /&gt;
|-&lt;br /&gt;
||[[SLetanque]]&lt;br /&gt;
||[[User:Babbage Linden|Babbage Linden]]&lt;br /&gt;
||An LSL petanque game.&lt;br /&gt;
|-&lt;br /&gt;
||[[SLURL HUD]]&lt;br /&gt;
||[[User:CodeBastard Redgrave|CodeBastard Redgrave]]&lt;br /&gt;
||Touch this HUD to get a SLURL through IM, email and floating text.&lt;br /&gt;
|-&lt;br /&gt;
||[[llSLURL (NewAge)]]&lt;br /&gt;
||[[User:Archile Azalee|Archile Azalee]]&lt;br /&gt;
||Newest most easiest way to create SLURL in a single function llSLURL&lt;br /&gt;
|-&lt;br /&gt;
||[[SL Mail V1.2]]&lt;br /&gt;
||[[User:Flennan Roffo|Flennan Roffo]]&lt;br /&gt;
||Second Life mail client V1.2 (released sept&#039;07). Send and receive mail from within Second Life from and to any address. With Address Book function and many chat commands. V1.3 is upcoming soon! &lt;br /&gt;
|-&lt;br /&gt;
||[[SL_NTPoHTTP_v1.1_client|SL NTPoHTTP client]]&lt;br /&gt;
|[[User:SignpostMarv Martin|SignpostMarv Martin]]&lt;br /&gt;
||Second Life Needs Time Parsing over Hyper Text Transfer Protocol&lt;br /&gt;
Emulates the function of [[llGetWallclock]] for any timezone by using SLOpenID&#039;s SLNTPoHTTP service. Also supports ISO 8601 and RFC 2822 timestamps. Script is dependent upon an external service operated by the author!&lt;br /&gt;
|-&lt;br /&gt;
||[[Smooth Rotating Door]]&lt;br /&gt;
|[[User:Toy Wylie|Toy Wylie]]&lt;br /&gt;
||A script for doors that open and close smoothly using llTargetOmega&lt;br /&gt;
|-&lt;br /&gt;
||[[Smooth Sliding Door]]&lt;br /&gt;
|[[User:SimonT Quinnell|SimonT Quinnell]]&lt;br /&gt;
||A script for sliding doors that open and close smoothly using [[llMoveToTarget]]. Asjusts for prim orientation.&lt;br /&gt;
|-&lt;br /&gt;
||[[Speed Tester]]&lt;br /&gt;
||{{User|Bobbyb30 Swashbuckler}}&lt;br /&gt;
||Similar to the [[Efficiency Tester]], this script allows you to test the speed of a particular function or snippet with multiple trials giving min/max/avg/median and the standard deviation.&lt;br /&gt;
|-&lt;br /&gt;
||[[Spiral Staircase Generator]]&lt;br /&gt;
|[[User:Meyermagic Salome|Meyermagic Salome]]&lt;br /&gt;
||Generates nice looking spiral staircases without much hassle.&lt;br /&gt;
|-&lt;br /&gt;
||[[Static URL&#039;s for HTTP-In Service|Static_URLs]]&lt;br /&gt;
||[[User:Jor3l Boa|Jor3l Boa]]&lt;br /&gt;
||How to generate a static url for HTTP-In temporal limitations.&lt;br /&gt;
|-&lt;br /&gt;
||[[String Compare]]&lt;br /&gt;
||[[User:Xaviar Czervik|Xaviar Czervik]]&lt;br /&gt;
||Compares two strings and reliably returns either 1, -1, or 0 if they are the same.&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Void_Singer/Functions#String:_Find_Last_Index|String: Reverse]]&lt;br /&gt;
||[[User:Void Singer|Void Singer]]&lt;br /&gt;
||Returns an input string in reverse order&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Void_Singer/Functions#String:_Get_Reverse_Order|String: Last Index]]&lt;br /&gt;
||[[User:Void Singer|Void Singer]]&lt;br /&gt;
||Returns the last index of search found in string (the backward equivalent of [[llSubStringIndex]])&lt;br /&gt;
|-&lt;br /&gt;
||[[Tail Messages (NewAge)]]&lt;br /&gt;
||[[User:Asia Snowfall|Asia Snowfall]]&lt;br /&gt;
||A super nice easy to use script for those non-scripters out there! NewAge coding has done it again where you no longer need to scroll up and down adding buttons and adding messages, Features a tag system (you&#039;ll see what i mean :P)&lt;br /&gt;
|-&lt;br /&gt;
||[[Teleport HUD]]&lt;br /&gt;
||[[User:Jesse Barnett|Jesse Barnett]]&lt;br /&gt;
||WORKS TO 4096 METERS!! Very user friendly teleport HUD. Add destinations by touching &amp;quot;Add&amp;quot; &amp;amp; naming destination in chat. Automatically gets sim name and coordinates. Will only display the destinations in the sim you are currently in. Demonstrates more advanced list manipulation and stride functions.&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Void_Singer/Programs#v7-D_Enh._Landmark-2-Map|Teleporter (landmark based)]]&lt;br /&gt;
||[[User:Void_Singer|Void_Singer]]&lt;br /&gt;
||Uses Landmarks to offer map teleports. Works as Hud or inworld objects, just drop in landmarks and go.&lt;br /&gt;
|-&lt;br /&gt;
||[[Text_Scroller|Text Scroller]]&lt;br /&gt;
||[[User:Fred_Gandt|Fred Gandt]]&lt;br /&gt;
||A simple text display object that scrolls text (applied as a texture) from right to left (like &#039;&#039;those&#039;&#039; LED signs) in a continuous loop.&lt;br /&gt;
|-&lt;br /&gt;
||[[Bobbyb&#039;s texture changer|Texture Changers]]&lt;br /&gt;
||{{User|Bobbyb30 Swashbuckler}}&lt;br /&gt;
||A collection of texture changing scripts.&lt;br /&gt;
|-&lt;br /&gt;
||[[Texture Menu Management|Texture Management]]&lt;br /&gt;
||[[User:Revolution Perenti|Revolution Perenti]]&lt;br /&gt;
||Dialog Menu based Texture Selection.&lt;br /&gt;
|-&lt;br /&gt;
||[[Tic Tac Toe]]&lt;br /&gt;
||[[User:CG Linden|CG Linden]]&lt;br /&gt;
||Step by step demo on how to implement a larger scripting project&lt;br /&gt;
|-&lt;br /&gt;
||[[TightList]]&lt;br /&gt;
||[[User:Strife Onizuka|Revolution Perenti]]&lt;br /&gt;
||Tight List is a family of functions for encoding lists as strings and then decoding them back into lists.&lt;br /&gt;
There are two flavors: TightList and TightListType. TightListType preserves types and uses a 6 char header, while TightList uses a 1 char header that doesn&#039;t preserve type. &lt;br /&gt;
|-&lt;br /&gt;
||[[Timer Module]]&lt;br /&gt;
||[[User:Isabelle Aquitaine|Isabelle Aquitaine]]&lt;br /&gt;
||Manage multiple timers via linked messages.&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Void_Singer/Functions#Unix_time_code_to_list_format|Timestamp:&amp;lt;br&amp;gt;Unix time code to list format]]&lt;br /&gt;
||[[User:Void Singer|Void Singer]]&lt;br /&gt;
||converts Unix timestamps to their [Y, M, D, h, m, s] equivalents (ex: 1234567890 to [2009, 2, 13, 3, 31, 30])&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Void_Singer/Functions#List_format_to_Unix_time_code.|Timestamp:&amp;lt;br&amp;gt;List format to Unix time code]]&lt;br /&gt;
||[[User:Void Singer|Void Singer]]&lt;br /&gt;
||converts [Y, M, D, h, m, s] timestamps to their Unix equivalents (ex: [2009, 2, 13, 3, 31, 30] to 1234567890)&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Void_Singer/Functions#Weekday_from_Unix_timestamp|Timestamp:&amp;lt;br&amp;gt;Weekday from Unix timestamp]]&lt;br /&gt;
||[[User:Void Singer|Void Singer]]&lt;br /&gt;
||Gets weekday from Unix timestamps (ex: &amp;quot;Friday&amp;quot; from 1234567890)&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Void_Singer/Functions#Weekday_from_.28_Y.2C_M.2C_D_.29_format|Timestamp:&amp;lt;br&amp;gt;Weekday from (Y, M, D) format]]&lt;br /&gt;
||[[User:Void Singer|Void Singer]]&lt;br /&gt;
||Gets weekday from (Y, M, D) timestamps (ex: &amp;quot;Friday&amp;quot; from (2009, 2, 13))&lt;br /&gt;
|-&lt;br /&gt;
||[[Touch A Quote]]&lt;br /&gt;
||[[User:CodeBastard Redgrave|CodeBastard Redgrave]]&lt;br /&gt;
||Touch an object to read quotes sequentially from a notecard&lt;br /&gt;
|-&lt;br /&gt;
||[[Touring Balloon]]&lt;br /&gt;
||[[User:Hank Ramos|Hank Ramos]]&lt;br /&gt;
||Automated touring balloon with many options from long ago.  Always seems to work on one SL release, and not the other.&lt;br /&gt;
|-&lt;br /&gt;
||[[Under Age Boot]]&lt;br /&gt;
|[[User:Chance Unknown|Chance Unknown]]&lt;br /&gt;
||Security device example to teleport home accounts below a minimum age limit; can be useful in combating free griefer accounts.&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Rolig_Loon/UNDO_PosRot|UNDO_PosRot]]&lt;br /&gt;
||[[User:Rolig Loon|Rolig Loon]]&lt;br /&gt;
||Allows user to undo position and rotation changes that have been made &amp;quot;manually&amp;quot; to any or all prims in a linkset.&lt;br /&gt;
|-&lt;br /&gt;
||[[Universal Translator]]&lt;br /&gt;
|[[User:Hank Ramos|Hank Ramos]]&lt;br /&gt;
||Chat listener that handles seamless translation of public chat between 50+ written languages without the need for configuration.  Handles numerous avatars, auto-detects languages, and works together with multiple copies of translators to spread-workload using a sophisticated back-end communications sub-system.&lt;br /&gt;
|-&lt;br /&gt;
||[[Unix2DateTime]]&lt;br /&gt;
|[[User:Flennan Roffo|Flennan Roffo]]&lt;br /&gt;
||Conversion from Unix time ([[llGetUnixTime]]()) to date and time string and vice versa.&lt;br /&gt;
|-&lt;br /&gt;
||[[Unmutable Descript Nagger]]&lt;br /&gt;
|[[User:Bobbyb30 Zohari|Bobbyb30 Zohari]]&lt;br /&gt;
||To nag avatars to take off their scripted attatchments.&lt;br /&gt;
|-&lt;br /&gt;
||[[Unpacker On Rez (NewAge)]]&lt;br /&gt;
|[[User:Asia Snowfall|Asia Snowfall]]&lt;br /&gt;
||A very simple to use script for all you creators out there, This script will enable you to send out boxed items and make it easier for users to unpack, Also features auto die on completion. Very simple to configure!&lt;br /&gt;
|-&lt;br /&gt;
||[[Unpacker On Touch (NewAge)]]&lt;br /&gt;
|[[User:Asia Snowfall|Asia Snowfall]]&lt;br /&gt;
||Exactly like Unpacker On Rez, But changed some things about to make it Unpack On Touch&lt;br /&gt;
|-&lt;br /&gt;
||[[Update distributor]]&lt;br /&gt;
|[[User:Dale Innis|Dale Innis]]&lt;br /&gt;
||Distribute an object (like a project update) to a list of people named in a notecard.&lt;br /&gt;
|-&lt;br /&gt;
||[[UUID2Channel]]&lt;br /&gt;
||[[User:Project Neox|Project Neox]]&lt;br /&gt;
||Optimised version of the original key2channel generators.&lt;br /&gt;
|-&lt;br /&gt;
||[[UUID Song Generator]]&lt;br /&gt;
||{{User|Sendao Goodman}}&lt;br /&gt;
||Translates a UUID into a simple song and plays it.&lt;br /&gt;
|-&lt;br /&gt;
||[[VirtualID_URLMap|VirtualID URL Mapper for HTTP-in]]&lt;br /&gt;
|[[User:Cenji Neutra|Cenji Neutra]]&lt;br /&gt;
||A script showing how to setup a static URL of the form &amp;lt;your-alias&amp;gt;.obj.virtualid.info which maps to the dynamic HTTP-in URL LSL generates and keeps it up-to-date.&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Void_Singer/Programs#v7-D_Advanced_Visitor_Greeter|Visitor Greeter]]&lt;br /&gt;
|[[User:Void_Singer|Void_Singer]]&lt;br /&gt;
||Reduced spam visitor greeter, highly configurable, easy to modify.&lt;br /&gt;
|-&lt;br /&gt;
||[[Visitor Logger (Web/Basic) ]]&lt;br /&gt;
|[[User:Buddy Sprocket|Buddy Sprocket]]&lt;br /&gt;
||A very basic visitor logger - log visitors in SL to a text file on your web-site.&lt;br /&gt;
|-&lt;br /&gt;
||[[Vote Simple]]&lt;br /&gt;
|[[User:JB_Kraft|JB Kraft]]&lt;br /&gt;
||Simple vote collector. One avi, one vote.&lt;br /&gt;
|-&lt;br /&gt;
||[[Walking Sound (NewAge)]]&lt;br /&gt;
|[[User:Asia Snowfall|Asia Snowfall]]&lt;br /&gt;
||Very powerful walking sound script, Featuring customer ability to add their own sounds with the API Sound Feature!&lt;br /&gt;
|-&lt;br /&gt;
||[[WarpPos]]&lt;br /&gt;
|[[User:Keknehv Psaltery|Keknehv Psaltery]]&lt;br /&gt;
||Non-physical movement without the 10m limit.&lt;br /&gt;
|-&lt;br /&gt;
||[[Wiki3DBuilder]] [[Wiki3DBuilder1.0]]&lt;br /&gt;
||[[User:Salahzar Stenvaag|Salahzar Stenvaag]] &lt;br /&gt;
||Allows a group of people to collectively build up complex 3D mindmaps with connected concepts. Uses particles for connections and low prim usage. Nodes can be textured, colored, changed form size moved collectively by everybody and can distribute notecards, landmarks, URL, objects, textures. I provide two version 0.1 and 1.0 and probably next version will communicate with moodle and sloodle external websites.&lt;br /&gt;
|-&lt;br /&gt;
||[[Window Control]]&lt;br /&gt;
||[[User:Emmas Seetan|Emmas Seetan]]&lt;br /&gt;
||For window opacity, helpful for buildings.&lt;br /&gt;
|-&lt;br /&gt;
||[[WHMcs SecondLife plugin]]&lt;br /&gt;
|[[User:Alicia Sautereau|Alicia Sautereau]]&lt;br /&gt;
||Linden Dollar payment plugin for the WHMcs hosting portal.&lt;br /&gt;
|-&lt;br /&gt;
||[[XTEA Strong Encryption Implementation]]&lt;br /&gt;
||[[User:Morse Dillon|Morse Dillon]]&lt;br /&gt;
||An LSL implementation of XTEA (eXtended Tiny Encryption Algorithm).  This is the first known public release of a &#039;real&#039; strong encryption implementation in LSL and is released under the GNU General Public License (GPL).&lt;br /&gt;
|-&lt;br /&gt;
||[[XyText 1.5|XyText]]&lt;br /&gt;
||[[User:Xylor Baysklef|Xylor Baysklef]]&lt;br /&gt;
|| Display text (up to 10 characters) on a prim. Use as many prims as desired.&lt;br /&gt;
|-&lt;br /&gt;
||[[XyyyyzText|XyyyyzText]]&lt;br /&gt;
||[[User:Criz Collins|Criz Collins]]&lt;br /&gt;
|| Display text (up to 10 characters) on a prim. Displays different text for each line instead of one single text, that will be broken into the next lines. Watch here for what that means: http://screencast.com/t/1wMLujLcEO&lt;br /&gt;
|-&lt;br /&gt;
||[[XyzzyText|XyzzyText]]&lt;br /&gt;
||[[User:Thraxis Epsilon|Thraxis Epsilon]] and [[User:Gigs Taggart|Gigs Taggart]]&lt;br /&gt;
|| Display text (up to 10 characters) on a prim. Way more efficient than XyText.&lt;br /&gt;
|-&lt;br /&gt;
||[[Youtube TV]]&lt;br /&gt;
||[[User:Morgam Biedermann|Morgam Biedermann]]&lt;br /&gt;
||Watch your favorite Youtube videos / auto set the texture to the parcel media texture.&lt;br /&gt;
|-&lt;br /&gt;
||[[Zero Lag Poseball]]&lt;br /&gt;
||[[User:Jippen Faddoul|Jippen Faddoul]] and [[User:Daemonika Nightfire|Daemonika Nightfire]]&lt;br /&gt;
||A simple poseball with no lag&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;box&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Support Script Library==&lt;br /&gt;
These are scripts in other languages, intended to be run on other systems that support scripts written in LSL&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;sortable&amp;quot; {{Prettytable}}&lt;br /&gt;
|- {{Hl2}}&lt;br /&gt;
! &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Creator&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Description&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|| [[User:Ina Centaur/UUID/calimg.api|Calendar Image UUID API]]&lt;br /&gt;
|| [[User:Ina Centaur|Ina Centaur]]&lt;br /&gt;
|| API that gives the UUID of an image of a calendar image give month and year arguments.&lt;br /&gt;
|-&lt;br /&gt;
||[[HTTP Post request to a PHP server]]&lt;br /&gt;
||[[User:Corto Maltese|Corto Maltese]]&lt;br /&gt;
|| This small library allows you to make simple POST requests to your website. The libraries allow you to get your request through the variable $_POST on the server. It also include a basic security mechanism aimed to stop hacking. Comprises of LSL client script and PHP server script.&lt;br /&gt;
|-&lt;br /&gt;
|| [[User:Ina Centaur/PHP/k2n.php|Key2Name.php]]&lt;br /&gt;
|| [[User:Ina Centaur|Ina Centaur]]&lt;br /&gt;
|| Get Avatar&#039;s Name using Second Life search service. (like in LSL Key2Name)&lt;br /&gt;
|-&lt;br /&gt;
|| [[Lame_Object_DNS_and_Cross_Sim_Messaging|Lame Object DNS and Cross Sim Messaging]]&lt;br /&gt;
|| [[User:Liandra Ceawlin|Liandra Ceawlin]]&lt;br /&gt;
|| Simple, cheeseball method of doing cross-sim communications with http-in and an external object DNS service.&lt;br /&gt;
|-&lt;br /&gt;
|| [[User:SignpostMarv Martin/PHP/llXorBase64StringsCorrect|llXorBase64StringsCorrect]]&lt;br /&gt;
|| [[User:SignpostMarv Martin|SignpostMarv Martin]]&lt;br /&gt;
|| An implementation of [[llXorBase64StringsCorrect]] in PHP- should be useful if you&#039;re using llXorBase64StringsCorrect to do cryptography work in LSL2 and posting it out to the web via [[llHTTPRequest]].&lt;br /&gt;
|-&lt;br /&gt;
|| [[User:SignpostMarv Martin/PHP/lsl fu.php|lsl_fu.php]]&lt;br /&gt;
|| [[User:SignpostMarv Martin|SignpostMarv Martin]]&lt;br /&gt;
|| A basic OOP&#039;d PHP Class containing VeloxSeverine&#039;s $_POST fixer and Marv&#039;s own eccentric ideas for &amp;quot;fixing&amp;quot; things.&lt;br /&gt;
|-&lt;br /&gt;
|| [[User:Jor3l Boa/PHP/n2k.php|Name2Key.php]]&lt;br /&gt;
|| [[User:Jor3l Boa|Jor3l Boa]]&lt;br /&gt;
|| Get Avatar&#039;s UUID using Second Life search service. (like in LSL Name2Key)&lt;br /&gt;
|-&lt;br /&gt;
|| [[Public_Object_DNS|Public Object DNS]]&lt;br /&gt;
|| [[User:Liandra Ceawlin|Liandra Ceawlin]]&lt;br /&gt;
|| Public object DNS-like system running on GAE, for http-in. Hopefully scalable enough for wide-spread usage.&lt;br /&gt;
|-&lt;br /&gt;
|| Silo&lt;br /&gt;
|| [[User:Zero_Linden|Zero Linden]]&lt;br /&gt;
|| General purpose data store in PHP.  Use this to persist arbitrary data from LSL via [[llHTTPRequest]].  See:&lt;br /&gt;
* [http://www.notabene-sl.com/misc/silo-README.txt README] file&lt;br /&gt;
* [http://www.notabene-sl.com/misc/silo.tgz silo.tgz] tarball&lt;br /&gt;
* forum post [http://forums-archive.secondlife.com/54/69/119570/1.html Announcement].&lt;br /&gt;
|-&lt;br /&gt;
||[[Silverday ObjectDNS]]&lt;br /&gt;
||[[User:Till Stirling|Till Stirling]]&lt;br /&gt;
||Dynamic Object-DNS-System to provide dynamic mapping of LSL-URLs to persistent domains. Features include redirect service, password protected domains, write protected domains, LSL-API for all necessary functions, optional web-interface.&lt;br /&gt;
|-&lt;br /&gt;
|| [http://aubretec.com/products/sldb SLDB]&lt;br /&gt;
|| [[User:Luc Aubret|Luc Aubret]]&lt;br /&gt;
|| Flexible web database storage using PHP/MySQL.  Used to store per-user field/value pairs from in-world objects using [[llHTTPRequest]]. &lt;br /&gt;
* [http://aubretec.com/support/manuals/sldbkit/ Implementation Guide]&lt;br /&gt;
* [http://aubretec.com/wp-content/uploads/2009/05/sldb.zip Download] (.zip, 12kb)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 0.5em&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Articles&#039;&#039;&#039;&lt;br /&gt;
*[[:Category:LSL Examples| Examples]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{LSLC|}}&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=Category:LSL_Library&amp;diff=1021593</id>
		<title>Category:LSL Library</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=Category:LSL_Library&amp;diff=1021593"/>
		<updated>2010-09-01T05:18:53Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* LSL Script Library */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{LSL Header|ml=*}}{{RightToc}}&lt;br /&gt;
==Script Library==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;box&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 0.5em 0.5em 1.5em&amp;quot;&amp;gt;&lt;br /&gt;
Come to this page to see complex examples that show how to combine parts of LSL.&lt;br /&gt;
&lt;br /&gt;
Go to the [[:Category:LSL Examples|LSL Examples]] page to see brief examples of how to use parts of LSL &lt;br /&gt;
&lt;br /&gt;
Why collect complex examples here? Well, ...&lt;br /&gt;
&lt;br /&gt;
There are many [[script|scripts]] that have become buried in the [http://forums-archive.secondlife.com/15/1.html Scripting Library archive] or the newer [https://blogs.secondlife.com/community/forums/scripting_library?view=discussions Scripting Library forum], were lost with the death of the early scripting forums, or sit idle in [[inventory|inventories]] that could be useful and should be more accessible.&lt;br /&gt;
&lt;br /&gt;
Other scripters may be in the same situation. This wiki is a well-suited medium for a script library. Feel free to add your scripts to the script library by creating new pages for them and linking to those pages here.&lt;br /&gt;
&lt;br /&gt;
Note that there are many more scripts in the LSL Library here, but you can&#039;t get to them if you don&#039;t know they exist, because they are subpages now, instead of an automatically updated category.  Good luck searching.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;box&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Rules for posting: ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 0.5em&amp;quot;&amp;gt;&lt;br /&gt;
#Your script must be tested and working. If it&#039;s not, stick it in your user-space until it is. This is a list of working, usable scripts.&lt;br /&gt;
#Add a link to your script&#039;s page here. Link back to this page from your script&#039;s page. Start your page with &amp;lt;nowiki&amp;gt;{{LSL Header}}&amp;lt;/nowiki&amp;gt;.&lt;br /&gt;
#Do not add scripts that duplicate the same functionality as an existing script or built in {{LSLGC|Functions|function}}. If yours does, explain why.&lt;br /&gt;
#Do not list simple scripts here. Include those among the [[:Category:LSL Examples|LSL Examples]] instead.&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;box&amp;quot;&amp;gt;&lt;br /&gt;
==LSL Script Library==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;sortable&amp;quot; {{Prettytable}}&lt;br /&gt;
|- {{Hl2}}&lt;br /&gt;
! &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Creator&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Description&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
||[[1st necessity of SL]]&lt;br /&gt;
||[[User:Beer Dailey|Beer Dailey]]&lt;br /&gt;
||Monitors for avatars and (de)activates scripts states to control script performance/lag.&lt;br /&gt;
|-&lt;br /&gt;
||[[3D Radar]]&lt;br /&gt;
||[[User:Jesse Barnett|Jesse Barnett]]&lt;br /&gt;
||Rezzes a ball for each avatar in range. Each ball tracks its own AV and displays distance.&lt;br /&gt;
|-&lt;br /&gt;
||[[Access (NewAge)]]&lt;br /&gt;
||[[User:Asia Snowfall|Asia Snowfall]]&lt;br /&gt;
||An easy to use script for permissions on who can use the script, Public/Group/Owner&lt;br /&gt;
|-&lt;br /&gt;
||[[Aim Detection]]&lt;br /&gt;
||[[User:Han Shuffle|Han Shuffle]]&lt;br /&gt;
||Monitors for avatars and reports back to owner about who is aiming at them.&lt;br /&gt;
|-&lt;br /&gt;
||[[AntiDelay Node]]&lt;br /&gt;
||[[User:Xaviar Czervik|Xaviar Czervik]]&lt;br /&gt;
||Uses [[llMessageLinked]] to stop those pesky delays.&lt;br /&gt;
|-&lt;br /&gt;
||[[AO Overriding Pose Ball]]&lt;br /&gt;
||[[User:Jesse Barnett|Jesse Barnett]]&lt;br /&gt;
||No more turning your AO off and on when you sit down&lt;br /&gt;
|-&lt;br /&gt;
||[[ARCFOUR Strong Encryption Implementation]]&lt;br /&gt;
||[[User:Nekow42 Zarf|Nekow42 Zarf]]&lt;br /&gt;
||An LSL implementation of ARCFOUR, the most popular stream cipher still in use. It is licensed under a Creative Commons Attribution 3.0 license.&lt;br /&gt;
|-&lt;br /&gt;
||[[Assembly Programming Language|Assembly-Like Programming Language]]&lt;br /&gt;
||[[User:Xaviar Czervik|Xaviar Czervik]]&lt;br /&gt;
||A compiler that runs assembly-like programs.&lt;br /&gt;
|-&lt;br /&gt;
||[[Associative Array Emulator|Associative Array (Dictionary) Emulator]]&lt;br /&gt;
||[[User:Alynna Vixen|Alynna Vixen]]&lt;br /&gt;
||This library provides a set of functions for using a list as an associative array where string based keys can refer to one or more variant elements.&lt;br /&gt;
|-&lt;br /&gt;
||[[AvatarFollower]]&lt;br /&gt;
||[[User:Dale Innis|Dale Innis]]&lt;br /&gt;
||Allows one avatar to automatically follow another.&lt;br /&gt;
|-&lt;br /&gt;
||[[Avatar Radar (NewAge)]]&lt;br /&gt;
||[[User:Asia Snowfall|Asia Snowfall]]&lt;br /&gt;
||Version 1.2; A nice new avatar radar script i just finish that you can place in your hud, Also features avatar locator&lt;br /&gt;
|-&lt;br /&gt;
||[[Base2Dec]]&lt;br /&gt;
||[[User:Siann Beck|Siann Beck]]&lt;br /&gt;
||Convert a number to decimal from any base.&lt;br /&gt;
|-&lt;br /&gt;
||[[BaseN]]&lt;br /&gt;
||[[User:Nexii Malthus|Nexii Malthus]]&lt;br /&gt;
||Variable base compression, dynamically maps to usable UTF code points.&lt;br /&gt;
|-&lt;br /&gt;
||[[Basic A-Star Pathfinder]]&lt;br /&gt;
||[[User:Nexii Malthus|Nexii Malthus]]&lt;br /&gt;
|| My own interpretation of A Star into a highly efficient algorithmn&lt;br /&gt;
|-&lt;br /&gt;
||[[Basic Encryption Modules]]&lt;br /&gt;
||[[User:Beverly Larkin|Beverly Larkin]]&lt;br /&gt;
||Basic encryption scripts, allows you to encrypt a float and shout it to another prim on a randomly chosen channel.&lt;br /&gt;
|-&lt;br /&gt;
||[[Be happy]]&lt;br /&gt;
||[[User:Emmas Seetan|Emmas Seetan]]&lt;br /&gt;
||Basic smile attachment script, makes your avatar smile.&lt;br /&gt;
|-&lt;br /&gt;
||[[Best Neighbor Ad Hiding Script|Best Neighbor]]&lt;br /&gt;
||[[User:Doran Zemlja|Doran Zemlja]]&lt;br /&gt;
||Reduce ad clutter by hiding ads when users are on their own land nearby.&lt;br /&gt;
|-&lt;br /&gt;
||[[BigNum|BigNum Library (RSA Encryption)]]&lt;br /&gt;
||[[User:Xaviar Czervik|Xaviar Czervik]]&lt;br /&gt;
||A BigNum Library for dealing with big numbers! Specialized for modular multiplication, and RSA encryption.&lt;br /&gt;
|-&lt;br /&gt;
||[[Binary Clock v1.1|Binary Clock]]&lt;br /&gt;
||[[User:Fox Diller|Fox Diller]]&lt;br /&gt;
||A Binary Clock.&lt;br /&gt;
|-&lt;br /&gt;
||[[BinaryDecimalConverter]]&lt;br /&gt;
||[[User:Soundless Smalls|Soundless Smalls]]&lt;br /&gt;
||Converts a binary value to a decimal value and vice versa.&lt;br /&gt;
|-&lt;br /&gt;
||[[Blacklist and Remote Kill|Blacklist and Remote Kill]]&lt;br /&gt;
||[[User:Chase Quinnell|Chase Quinnell]]&lt;br /&gt;
||Blacklist(denial of use) or a creator kill script(can delete someone&#039;s item by command on private channel)&lt;br /&gt;
|-&lt;br /&gt;
||[[User_talk:Rolig_Loon/Bookmark_URLs|Bookmark URLs]]&lt;br /&gt;
||[[User:Rolig Loon|Rolig Loon]]&lt;br /&gt;
||Dialog driven HUD reads bookmarked URLs from notecards and navigates directly to them with user&#039;s in-world browser.&lt;br /&gt;
|-&lt;br /&gt;
||[[Builders Buddy|Builder&#039;s Buddy Tool]]&lt;br /&gt;
||[[User:Newfie Pendragon|Newfie Pendragon]]&lt;br /&gt;
||Script to easily move/rotate large builds that exceed the linkable size limit (30 meters).&lt;br /&gt;
|-&lt;br /&gt;
||[[Button Click Detector]]&lt;br /&gt;
||{{User|Sendao Goodman}}&lt;br /&gt;
||Use [[llDetectedTouchUV]] to determine which button was pressed on a texture.&lt;br /&gt;
|-&lt;br /&gt;
||[[Camera following prim]]&lt;br /&gt;
||[[User:Dale Innis|Dale Innis]]&lt;br /&gt;
||Pair of scripts to make a prim follow your camera position around (for lights etc).&lt;br /&gt;
|-&lt;br /&gt;
||[[Camera Sync]]&lt;br /&gt;
||[[User:Meyermagic Salome|Meyermagic Salome]] and [[User:Nomad Padar|Nomad Padar]]&lt;br /&gt;
||A system to synchronize the cameras of two avatars.&lt;br /&gt;
|-&lt;br /&gt;
||[[Chatbot]]&lt;br /&gt;
||Anonymous&lt;br /&gt;
||Compile and run the LSL you type on a channel, faster than you can thru the 2007-08 SL GUI.&lt;br /&gt;
|-&lt;br /&gt;
||[[Chat Logger (GPL)]]&lt;br /&gt;
||[[User:Nobody Fugazi|Nobody Fugazi]]&lt;br /&gt;
||Chat logger which requests permission from participants before recording them.&lt;br /&gt;
|-&lt;br /&gt;
||[[Chat_Relay|Chat Relay]]&lt;br /&gt;
||[[User:grumble Loudon|grumble Loudon]]&lt;br /&gt;
||A Chat relay which can be routed using a path header and won&#039;t echo.&lt;br /&gt;
|-&lt;br /&gt;
||[[ClickAndDrag]]&lt;br /&gt;
||[[User:Nexii Malthus|Nexii Malthus]]&lt;br /&gt;
|| Click and Drag user interface elements using dynamic feedback&lt;br /&gt;
|-&lt;br /&gt;
||[[Code Racer]]&lt;br /&gt;
||Anonymous&lt;br /&gt;
||Race two versions of code forever, to see which runs faster.&lt;br /&gt;
|-&lt;br /&gt;
||[[Code Sizer]]&lt;br /&gt;
||Anonymous&lt;br /&gt;
||Count the bytes compiled from source code, to measure how to write small code.&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Void_Singer/Programs#v7-D_Enh._Color_Picker|Color Changer]]&lt;br /&gt;
||[[User:Void_Singer|Void_Singer]]&lt;br /&gt;
||Dialog driven color changer. Supports 16million+ colors, web color codes, multiple targeted prims, with save and recall.&lt;br /&gt;
|-&lt;br /&gt;
||[[Color Changer|Color Changer Plus]]&lt;br /&gt;
||[[User:Neo Calcutt|Neo Calcutt]]&lt;br /&gt;
||A color changer with 14 colors, a random function, and a custom vector function.&lt;br /&gt;
|-&lt;br /&gt;
||[[Color conversion scripts|Color Conversion]]&lt;br /&gt;
||[[User:Sally LaSalle|Sally LaSalle]]&lt;br /&gt;
||Convert between Red Green Blue (RGB) and Hue Saturation Value (HSV).&lt;br /&gt;
|-&lt;br /&gt;
||[[ColorConvert]]&lt;br /&gt;
||[[User:Siann Beck|Siann Beck]]&lt;br /&gt;
||Convert color values to vector from RGB, hex or HTML color name.&lt;br /&gt;
|-&lt;br /&gt;
||[[Color script]]&lt;br /&gt;
||[[User:Masakazu Kojima|Masakazu Kojima]]&lt;br /&gt;
||Script for changing colors trough a listener with pre-defined colors.&lt;br /&gt;
|-&lt;br /&gt;
||[[Library Combined Library|Combined Library]]&lt;br /&gt;
||[[User:Strife Onizuka|Strife Onizuka]]&lt;br /&gt;
||Library of mostly encoding and decoding functions, some more useful then others.&lt;br /&gt;
* String functions: Replace / Trim right / Trim left / Trim both&lt;br /&gt;
* Unicode conversion: UTF8 to Unicode / Unicode to UTF8&lt;br /&gt;
* List functions: Replace / Compare&lt;br /&gt;
|-&lt;br /&gt;
||[[Computer:jaycoonlanguage]]&lt;br /&gt;
||[[User:jayco121 Bing|jayco121 Bing]]&lt;br /&gt;
|| A language written in LSL that is meant for my computer (available at the shop).&lt;br /&gt;
|-&lt;br /&gt;
||[[Read Note Card Configuration|Configuration Notecard Reader]]&lt;br /&gt;
||{{User|Dedric Mauriac}}&lt;br /&gt;
||A script to read configuration information from a notecard. Parses notecard to extract key words and their assigned values. Allows for comment lines and many more useful features.&lt;br /&gt;
|-&lt;br /&gt;
||[[AdvancedNotecardReader|Configuration Notecard Reader (advanced)]]&lt;br /&gt;
||[[Lear Cale|Lear Cale]]&lt;br /&gt;
||Robust configuration notecard reader; supports multiple notecards with same suffix, handles reconfig on inventory change, and does not usurp the default state.&lt;br /&gt;
|-&lt;br /&gt;
||[[Library_Chat_Relay|Conversation Relay]]&lt;br /&gt;
||[[User:Jippen Faddoul|Jippen Faddoul]]&lt;br /&gt;
||Chat relay which requests permission from participants before relaying their messages. Also includes their attachments. (ToS compliant).&lt;br /&gt;
|-&lt;br /&gt;
||[[Curtain script]]&lt;br /&gt;
||[[User:Zilla Larsson|Zilla Larsson]]&lt;br /&gt;
||A simple script to retract/stretch curtains, blinds, bedcovers and more&lt;br /&gt;
|-&lt;br /&gt;
||[[Dataserver API]]&lt;br /&gt;
||[[User:Revolution Perenti|Revolution Perenti]]&lt;br /&gt;
||Dataserver Framework for Notecards.&lt;br /&gt;
|-&lt;br /&gt;
||[[Date Library]]&lt;br /&gt;
||[[User:Corto Maltese|Corto Maltese]]&lt;br /&gt;
|| Date library, based on number of day since march 3rd 1600, can be used to calculate weekday, date differences, and date offset, and date formating.&lt;br /&gt;
|-&lt;br /&gt;
||[[Day of the Week]]&lt;br /&gt;
||[[User:DoteDote Edison|DoteDote Edison]]&lt;br /&gt;
||Function to get day of the week from [[llGetUnixTime]].&lt;br /&gt;
|-&lt;br /&gt;
||[[Days in Month]]&lt;br /&gt;
||[[User:IntLibber Brautigan begin_of_the_skype_highlighting     end_of_the_skype_highlighting begin_of_the_skype_highlighting     end_of_the_skype_highlighting|IntLibber Brautigan]]&lt;br /&gt;
||Clicking on it returns the number of days in the present month. Useful for scripting calendars and tier systems that need to know the number of days in the month at hand or to calculate for any month. Even adjusts for leap years.&lt;br /&gt;
|-&lt;br /&gt;
||[[Deed Tools]]&lt;br /&gt;
||[[User:Falados Kapuskas|Falados Kapuskas]]&lt;br /&gt;
||Tools that allow the creator to modify Group-Owned (Deeded) Objects via chat.&lt;br /&gt;
|-&lt;br /&gt;
||[[Describe Chatter]]&lt;br /&gt;
||Anonymous&lt;br /&gt;
||Chat to see yourself as others do.&lt;br /&gt;
|-&lt;br /&gt;
||[[Dialog Control]]&lt;br /&gt;
||[[User:Nargus Asturias|Nargus Asturias]]&lt;br /&gt;
|| A (not-so) simple dialog &amp;amp; menu control script. Call dialog and receive selected value via [[link_message]](), with built-in timer and [[link_message]]() notification on time out. Supports multi-pages dialog and numeric property dialog. Button text and dialog&#039;s returned value can differ.&lt;br /&gt;
Latest version also has [[Dialog Menus Control]] built-in; which allow multi-level menus through SL dialog system.&lt;br /&gt;
|-&lt;br /&gt;
||[[MultiUser_Dialog_Handler|Dialog Handler (multiuser)]]&lt;br /&gt;
||[[User:SimonT Quinnell|SimonT Quinnell]]&lt;br /&gt;
|| Menu dialog handler that supports multiple menus open at once from the single script. Displays multi-page menus if necessary as well as allowing for fixed header and footer buttons.&lt;br /&gt;
|-&lt;br /&gt;
||[[Dialog NumberPad|Dialog Number Pad]]&lt;br /&gt;
||[[User:DoteDote Edison|DoteDote Edison]]&lt;br /&gt;
||Use a dialog to accept positive integer input from users.&lt;br /&gt;
|-&lt;br /&gt;
||[[Displayer Script]]&lt;br /&gt;
||[[User:Akinori Kimagawa|Akinori Kimagawa]]&lt;br /&gt;
||[[Display Words On Top Of An Object]]&lt;br /&gt;
|-&lt;br /&gt;
||[[Drink script]]&lt;br /&gt;
||[[User:Emmas Seetan|Emmas Seetan]]&lt;br /&gt;
||Used mainly for food and drink in Second Life.&lt;br /&gt;
|-&lt;br /&gt;
||[[TOXDropBox|DropBox]]&lt;br /&gt;
||[[User:Dimentox Travanti|Dimentox Travanti]]&lt;br /&gt;
|| This is a Drop box which allows people to drop certain items in a object &amp;amp; has many config options.&lt;br /&gt;
|-&lt;br /&gt;
||[[Efficiency Tester]]&lt;br /&gt;
||[[User:Xaviar Czervik|Xaviar Czervik]]&lt;br /&gt;
||Tests the speed of a function.&lt;br /&gt;
|-&lt;br /&gt;
||[[Email-to-IM|Email2IM]]&lt;br /&gt;
||[[User:DoteDote Edison|DoteDote Edison]]&lt;br /&gt;
||Send IMs to SL friends via [[email]] (translate emails from friends into IMs).&lt;br /&gt;
|-&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Kireji_Haiku/SIMchat_headset|Encrypted Region-wide chat]]&lt;br /&gt;
||&#039;&#039;&#039;[[User:Kireji Haiku|Kireji Haiku]]&#039;&#039;&#039; &amp;lt;sup&amp;gt;&amp;lt;small&amp;gt;([[User talk:Kireji Haiku|talk]]|[[Special:Contributions/Kireji Haiku|contribs]])&amp;lt;/small&amp;gt;&amp;lt;/sup&amp;gt;&lt;br /&gt;
||Encrypted Region-wide chat&lt;br /&gt;
|-&lt;br /&gt;
||[[ExplodingObjects]]&lt;br /&gt;
||[[User:Dale Innis|Dale Innis]]&lt;br /&gt;
||Causes an object (of the appropriate type) to explode on command.&lt;br /&gt;
|-&lt;br /&gt;
||[[FadeEasy]]&lt;br /&gt;
||[[User:Nika Rugani|Nika Rugani]]&lt;br /&gt;
||The easy way of fading objects in or out (Using llSetLinkAlpha)&lt;br /&gt;
|-&lt;br /&gt;
||[[FastConeSpread]]&lt;br /&gt;
||[[User:Nexii Malthus|Nexii Malthus]]&lt;br /&gt;
|| Fast Algorithmn to achieve cone spread, main use in weaponry systems.&lt;br /&gt;
|-&lt;br /&gt;
||[[User:PixelProphet Lane/Scripts#Fast List Prim Contents|Fast List Prim Contents]]&lt;br /&gt;
||[[User:PixelProphet Lane|PixelProphet Lane]]&lt;br /&gt;
||Fast and efficient method to print Object Inventory (Name, Type and next Owner permissions)&lt;br /&gt;
|-&lt;br /&gt;
||[[Find Avatar Key|Find Avatar Key]]&lt;br /&gt;
||[[User:Huney Jewell|Huney Jewell]]&lt;br /&gt;
||Explores [[UUID]] of avatar whose name is said in local chat or who touches the prim.&lt;br /&gt;
|-&lt;br /&gt;
||[[First Name Letter Prize]]&lt;br /&gt;
||[[User:RaithSphere Whybrow|RaithSphere Whybrow]]&lt;br /&gt;
||Gives a prize if the person who sits on it&#039;s first letter of first name matches the random letter!&lt;br /&gt;
|-&lt;br /&gt;
||[[Fix Small Prims|Fix_Small_Prims]]&lt;br /&gt;
||[[User:Emma Nowhere|Emma Nowhere]]&lt;br /&gt;
||Finds and adjusts the smallest prims in a linkset so that it can be scaled down further.&lt;br /&gt;
|-&lt;br /&gt;
||[[Float2Hex]]&lt;br /&gt;
||[[User:Strife Onizuka|Strife Onizuka]]&lt;br /&gt;
||Very useful for transporting [[float|floats]].&lt;br /&gt;
|-&lt;br /&gt;
||[[Float Box Contents]]&lt;br /&gt;
||[[User:Rolig Loon|Rolig Loon]]&lt;br /&gt;
||Displays object inventory in hover text, identified by type and updated marquee-style.&lt;br /&gt;
|-&lt;br /&gt;
||[[Follower (script)|Follower]]&lt;br /&gt;
||Unknown, uploaded by [[User:Slik Swindlehurst|Slik Swindlehurst]]&lt;br /&gt;
||Makes an object follow the nearest person. Do not use for [[grief|griefing]].&lt;br /&gt;
|-&lt;br /&gt;
||[[GA Event Notifier]]&lt;br /&gt;
||[[User:Victor Hua|Victor Hua]]&lt;br /&gt;
||Gathers seven days event data from a Google calendar and display it through a simple interface. The lsl script can access several calendars at once through seperate php files. One file per calendar. Host the included php on your own web server.&lt;br /&gt;
|-&lt;br /&gt;
||[[Geometric|Geometric Library]]&lt;br /&gt;
||Community Project&lt;br /&gt;
||A substantial amount of various geometric functions for intersection and other purposes of 3D maths.&lt;br /&gt;
|-&lt;br /&gt;
||[[Get Profile Picture]]&lt;br /&gt;
||[[User:Valentine Foxdale|Valentine Foxdale]]&lt;br /&gt;
||Sets the texture of the object to profile picture of the person that touches ot&lt;br /&gt;
|-&lt;br /&gt;
||[[GetTimestampOffset]]&lt;br /&gt;
||[[User:Siann_Beck|Siann Beck]]&lt;br /&gt;
||Returns [[llGetTimestamp]]() with an hour offset.&lt;br /&gt;
|-&lt;br /&gt;
||[[Give InvItem every n hours]]&lt;br /&gt;
||[[User:Criz Collins|Criz Collins]]&lt;br /&gt;
||Will give an inventory item on touch only every n hours, even if somebody touches the object more than once. &lt;br /&gt;
|-&lt;br /&gt;
||[[Give random object]]&lt;br /&gt;
||[[User:Emmas Seetan|Emmas Seetan]]&lt;br /&gt;
||Touch to receive a random object in the prim&#039;s inventory&lt;br /&gt;
|-&lt;br /&gt;
||[[Google Charts]]&lt;br /&gt;
||[[User:Dedric Mauriac|Dedric Mauriac]]&lt;br /&gt;
||Create links to display raw data as a chart image.&lt;br /&gt;
|-&lt;br /&gt;
||[[Google_Translator]]&lt;br /&gt;
||[[User:Ugleh Ulrik|Ugleh Ulrik]]&lt;br /&gt;
||Translates spanish to english, and its simple to make it any other way.&lt;br /&gt;
|-&lt;br /&gt;
||[[Go transparent when walking]]&lt;br /&gt;
||[[User:Emmas Seetan|Emmas Seetan]]&lt;br /&gt;
||An attachment that goes invisible when you walk and visible when you don&#039;t walk.&lt;br /&gt;
|-&lt;br /&gt;
||[[Group Authorization]]&lt;br /&gt;
||[[User:Chase Quinnell|Chase Quinnell]]&lt;br /&gt;
||Checks to see if object is set to appropriate group (checks by [[Group key finder|group key]])&lt;br /&gt;
|-&lt;br /&gt;
||[[Group Information v1.0]]&lt;br /&gt;
||[[User:Tyrennic Rivera|Tyrennic Rivera]]&lt;br /&gt;
||When clicked the prim will show group information (set on the prim) from the official Second Life Search page.&lt;br /&gt;
|-&lt;br /&gt;
||[[Group key finder]]&lt;br /&gt;
||[[User:Emmas Seetan|Emmas Seetan]]&lt;br /&gt;
||Touch to find the key of the group&lt;br /&gt;
|-&lt;br /&gt;
||[[Group Privacy]]&lt;br /&gt;
||[[User:Chance Unknown|Chance Unknown]]&lt;br /&gt;
||Security device to insure members of a group can have a private area. Deactivates when nobody present.&lt;br /&gt;
|-&lt;br /&gt;
||[[Hello Avatar]]&lt;br /&gt;
||Linden Lab&lt;br /&gt;
||SL&#039;s default script.&lt;br /&gt;
|-&lt;br /&gt;
||[[Hello Avatar Companion]]&lt;br /&gt;
||[[Chase Quinnell]]&lt;br /&gt;
||Companion to the original [[Hello Avatar]] script&lt;br /&gt;
|-&lt;br /&gt;
||[[Hierarchics]]&lt;br /&gt;
||[[User:Nexii Malthus|Nexii Malthus]]&lt;br /&gt;
|| Advanced script to create an efficient self-aware hierarchic structure.&lt;br /&gt;
|-&lt;br /&gt;
||[[Hierarchics2]]&lt;br /&gt;
||[[User:Nexii Malthus|Nexii Malthus]]&lt;br /&gt;
|| Second generation, one script per object. Only two functions, can be embedded in others. Uses the new [[llSetLinkPrimitiveParamsFast]]/[[llGetLinkPrimitiveParams]] functions.&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Rolig_Loon/High-Capacity_Greeter-Counter|High-Capacity Greeter-Counter]]&lt;br /&gt;
||[[User:Rolig Loon|Rolig Loon]]&lt;br /&gt;
||Uses a memory compression algorithm to store hashed visitor UUID&#039;s in a string instead of using a list. &lt;br /&gt;
|-&lt;br /&gt;
||[[Holodeck]]&lt;br /&gt;
||[[User:Revolution Perenti|Revolution Perenti]]&lt;br /&gt;
||Home Rezzing System (Open Source).&lt;br /&gt;
|-&lt;br /&gt;
||[[Interpolation|Interpolation Library]]&lt;br /&gt;
||[[User:Nexii_Malthus|Nexii Malthus]]&lt;br /&gt;
||A small set of interpolation functions.&lt;br /&gt;
|-&lt;br /&gt;
||[[Intra-Region Update Server]]&lt;br /&gt;
||[[User:Emma_Nowhere|Emma Nowhere]]&lt;br /&gt;
||Centrally update objects such as Freeview screens or teleport pads within a region that are configured by notecards or contain modifiable objects or media assets.&lt;br /&gt;
|-&lt;br /&gt;
||[[Inventory_Based_Menu]]&lt;br /&gt;
||[[User:Revolution Perenti|Revolution Perenti]]&lt;br /&gt;
||Inventory Based Menu System.&lt;br /&gt;
|-&lt;br /&gt;
||[[iTunes RPC Email|iTunes RPC]]&lt;br /&gt;
||[[User:Fox Diller|Fox Diller]]&lt;br /&gt;
||iTunes RPC via LSL [[llEmail]] and [[llRemoteDataReply]].&lt;br /&gt;
|-&lt;br /&gt;
||[[Kilt Editor|Kilt / Skirt Editor]]&lt;br /&gt;
||[[User:Brangus Weir|Brangus Weir]]&lt;br /&gt;
|| This changes all the parameters of all the flexis in the link set in one swell foop!&lt;br /&gt;
|-&lt;br /&gt;
||[[Key Pad Door|Keypad Door]]&lt;br /&gt;
||[[User:Tdub Dowler|Tdub Dowler]]&lt;br /&gt;
|| Door and keypad with changeable code. Follow instructions carefully!&lt;br /&gt;
|-&lt;br /&gt;
||[[Last Sound System]]&lt;br /&gt;
||[[User:Babbage Linden|Babbage Linden]]&lt;br /&gt;
||An LSL [http://Last.fm Last.fm] client.&lt;br /&gt;
|-&lt;br /&gt;
||[[LibraryDisplayLandScreenshot]]&lt;br /&gt;
||[[User:Jon Desmoulins|Jon Desmoulins]]&lt;br /&gt;
||A modified version of LibraryDisplayProfilePic using the new [[PARCEL_DETAILS_ID]] Implemented in Server v1.36&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Daemonika_Nightfire/Scripts/LinkNumber-List_in_llSetLinkPrimitiveParamsFast|LinkNumber-List_in_llSetLinkPrimitiveParamsFast]]&lt;br /&gt;
||[[User:Daemonika Nightfire|Daemonika Nightfire]]&lt;br /&gt;
||Changed several prims in linkset with the same parameters, with a list. Without separate llSetLinkPrimitiveParams for each prim. (ideal for show/hide effects)&lt;br /&gt;
|-&lt;br /&gt;
||[[Linkset resizer]]&lt;br /&gt;
||[[User:Maestro Linden|Maestro Linden]]&lt;br /&gt;
||Systematically rescales a linkset by moving and resizing each prim (by using [[llGetLinkPrimitiveParams]] and [[llSetLinkPrimitiveParamsFast]])&lt;br /&gt;
|-&lt;br /&gt;
||[[Linkset resizer with menu]]&lt;br /&gt;
||[[User:Brilliant Scientist|Brilliant Scientist]]&lt;br /&gt;
||A menu-driven script that rescales a linkset by moving and resizing the prims using [[llGetLinkPrimitiveParams]] and [[llSetLinkPrimitiveParamsFast]] functions. Based on [[Linkset resizer]].&lt;br /&gt;
|-&lt;br /&gt;
||[[Linkset Resizer 2]]&lt;br /&gt;
||[[User:Emma Nowhere|Emma Nowhere]]&lt;br /&gt;
||A more user-friendly resizer script designed for either drop-in use by the end-user or builder or for use in products. Based on [[Fix Small Prims]].&lt;br /&gt;
|-&lt;br /&gt;
||[[List2CSV]]&lt;br /&gt;
||[[User:Kunnis Basiat|Kunnis Basiat]]&lt;br /&gt;
||List2CSV &amp;amp; CSV2List that include preserving type and escaping characters.&lt;br /&gt;
|-&lt;br /&gt;
||[[list_cast]]&lt;br /&gt;
||[[User:Fractured Crystal|Fractured Crystal]]&lt;br /&gt;
||Casts a list of strings into the type they appear to be. Designed for preprocessing user input for feeding into [[llSetPrimitiveParams]]&lt;br /&gt;
|-&lt;br /&gt;
||[[Listener Script]]&lt;br /&gt;
||[[User:Emmas Seetan|Emmas Seetan]]&lt;br /&gt;
||Use to [[listen]] to other people&#039;s conversations (Like spying)&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Void_Singer/Functions#List:_Find_Last_Index|List: Find Last Index]]&lt;br /&gt;
||[[User:Void Singer|Void Singer]]&lt;br /&gt;
||Returns the last index of searched item in a source list. (backwards version of [[llListFindList]])&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Void_Singer/Functions#List:_Multi-Find_Index_.28First_or_Last.29|List: Multi Find]]&lt;br /&gt;
||[[User:Void Singer|Void Singer]]&lt;br /&gt;
||Returns the first found index of multiple search items in a list. (Multi-item version of [[llListFindList]]. Fwd and Rev versions included)&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Void_Singer/Functions#List:_Get_Reverse_Order|List: Reverse]]&lt;br /&gt;
||[[User:Void Singer|Void Singer]]&lt;br /&gt;
||Returns an input List in reverse order&lt;br /&gt;
|-&lt;br /&gt;
||[[Load URL]]&lt;br /&gt;
||[[User:Emmas Seetan|Emmas Seetan]]&lt;br /&gt;
||Touch to get a dialog to visit the URL inside the script.&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Kephra_Nurmi/lsDancemachine|lsDancemachine]]&lt;br /&gt;
||[[User:Kephra_Nurmi|Kephra Nurmi]]&lt;br /&gt;
|| Low lag client server dancemachine.&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Kephra_Nurmi/lsDeejay|lsDeejay Home Edition]] &lt;br /&gt;
||[[User:Kephra_Nurmi|Kephra Nurmi]]&lt;br /&gt;
|| Client server media control for music, video, youtube, pictures, and texture animations.&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Kephra_Nurmi/lsDialog|lsDialog]]&lt;br /&gt;
||[[User:Kephra_Nurmi|Kephra Nurmi]]&lt;br /&gt;
|| Universal notecard driven menu dialog system.&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Kephra_Nurmi/lsDisplay|lsDisplay]]&lt;br /&gt;
||[[User:Kephra_Nurmi|Kephra Nurmi]]&lt;br /&gt;
|| Picture cycler with preloader.&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Kephra_Nurmi/lsDistributor|lsDistributor]]&lt;br /&gt;
||[[User:Kephra_Nurmi|Kephra Nurmi]]&lt;br /&gt;
|| Simple &#039;try&#039; and &#039;buy&#039; vendor system.&lt;br /&gt;
|-&lt;br /&gt;
||[[LSL_languageAPI]]&lt;br /&gt;
||[[User:Gypsy paz|Gypsy Paz]]&lt;br /&gt;
||Multi-lingual API from notecard based language files&lt;br /&gt;
|-&lt;br /&gt;
||[[Mandelbrot Explorer]]&lt;br /&gt;
||[[User:Babbage Linden|Babbage Linden]]&lt;br /&gt;
||An interactive fractal explorer.&lt;br /&gt;
|-&lt;br /&gt;
||[[Merge Sort]]&lt;br /&gt;
||[[User:Xaviar Czervik|Xaviar Czervik]]&lt;br /&gt;
||Implements a Merge Sort in LSL, however this code is 300(ish) times slower than [[llListSort]]. Don&#039;t use this in a script!&lt;br /&gt;
|-&lt;br /&gt;
||[[Minesweeper]]&lt;br /&gt;
||[[User:Xaviar Czervik|Xaviar Czervik]]&lt;br /&gt;
||A simple minesweeper game.&lt;br /&gt;
|-&lt;br /&gt;
||[[Morse Code]]&lt;br /&gt;
||{{User|Bobbyb30 Swashbuckler}}&lt;br /&gt;
||A script that allows for the conversion to and from morse code and can &amp;quot;play&amp;quot; morse code.&lt;br /&gt;
|-&lt;br /&gt;
||[[Multi Item Rezzer|Multi Item Rezzer]]&lt;br /&gt;
||[[User:Jesse Barnett|Jesse Barnett]]&lt;br /&gt;
||A rework of my old High Altitude Rezzer. Place the objects that you wish to choose from to be rezzed inside. Sit on the rezzer and pick the item and the height. It will go to target height and rez the object.&lt;br /&gt;
|-&lt;br /&gt;
||[[Multirezzer|Multirezzer (on collision)]]&lt;br /&gt;
||[[User:Beet Streeter|Beet Streeter]]&lt;br /&gt;
||Spawns up to 10 objects when the object containing the script collides with a user.&lt;br /&gt;
|-&lt;br /&gt;
||[[Name2Key in LSL]]&lt;br /&gt;
||[[User:Maeva Anatine|Maeva Anatine]]&lt;br /&gt;
||Get the Name2Key feature inside your scripts. Works even on lastly subscribed avatars.&lt;br /&gt;
|-&lt;br /&gt;
||[[No Auto-Return]]&lt;br /&gt;
||[[User:Bellla Clarity|Bella Clarity]]&lt;br /&gt;
||To stop your long and hard builds from getting returned in sandboxes (&#039;&#039;only single prims, though!&#039;&#039;).&lt;br /&gt;
|-&lt;br /&gt;
||[[No Auto-Return NR]]&lt;br /&gt;
||[[User:Jor3l Boa|Jor3l Boa]]&lt;br /&gt;
||This really works (29-05-09), needs a nearby region to do the trick and avoid auto return. (Tested on Blue and Rausch)&lt;br /&gt;
|-&lt;br /&gt;
||[[No Auto-Return (Multi)]]&lt;br /&gt;
||[[User:Xaviar Czervik|Xaviar Czervik]]&lt;br /&gt;
||A revision of Bella&#039;s that works for multi-prim objects.&lt;br /&gt;
|-&lt;br /&gt;
||[[No Limit Teleporter]]&lt;br /&gt;
||[[User:Morgam Biedermann|Morgam Biedermann]]&lt;br /&gt;
||Teleport to infinite altitudes (up to 4096m)&lt;br /&gt;
|-&lt;br /&gt;
||[[Object Size]]&lt;br /&gt;
||[[User:Chase Quinnell|Chase Quinnell]]&lt;br /&gt;
||Gets the dimensions and footprint of a linkset&lt;br /&gt;
|-&lt;br /&gt;
||[[Object to Data v1.3|Object to Data]]&lt;br /&gt;
||[[User:Xaviar Czervik|Xaviar Czervik]]&lt;br /&gt;
||Turns an object into text (and back). Allows people to transfer objects through notecards (or otherwise).&lt;br /&gt;
|-&lt;br /&gt;
||[[Online Indicator|Online Indicator]]&lt;br /&gt;
||[[User:Kristy Fanshaw|Kristy Fanshaw]]&lt;br /&gt;
||Will show if the user is online or not. Displays users profile picture and allows to send IM&#039;s to user. Also gives a link to open the users profile&lt;br /&gt;
|-&lt;br /&gt;
||[[Open Group Join]]&lt;br /&gt;
||[[User:Alicia Stella|Alicia Stella]]&lt;br /&gt;
||User Touches Object to Join Group from Group Info window, (no bot.)&lt;br /&gt;
|-&lt;br /&gt;
||[[Open Zyngo Skin Installer]]&lt;br /&gt;
||[[User:Tmzasz Luminos|Tmzasz Luminos]]&lt;br /&gt;
||A Simple Script designed to install skins on the popular Skill machines.&lt;br /&gt;
|-&lt;br /&gt;
||[[One Script, many doors]]&lt;br /&gt;
||[[User:Kyrah Abattoir|Kyrah Abattoir]]&lt;br /&gt;
||Door script able to manage more than 50 linked doors from a single script instance.&lt;br /&gt;
|-&lt;br /&gt;
||[[ParseString2List]]&lt;br /&gt;
||[[User:Strife Onizuka|Strife Onizuka]]&lt;br /&gt;
||Same as [[llParseString2List]] and [[llParseStringKeepNulls]], but not limited to 8 spacers or separators. Thus substitute a call to the [[llParseString2List]] and [[llParseStringKeepNulls]] functions by a call to [[Parse_String_To_List|ParseString2List]] whenever you have more than 8 separators or more than 8 spacers.&lt;br /&gt;
|-&lt;br /&gt;
||[[Password Generator]]&lt;br /&gt;
||[[User:Syntrax Canucci|Syntrax Canucci]]&lt;br /&gt;
||This is an over-complicated, semi-complex password generator, which goes through multiple steps.&lt;br /&gt;
|-&lt;br /&gt;
||[[Pathfinder]]&lt;br /&gt;
||[[User:Babbage Linden|Babbage Linden]]&lt;br /&gt;
||A potential field based pathfinding library.&lt;br /&gt;
|-&lt;br /&gt;
||[[Phantom Child]]&lt;br /&gt;
||[[User:Aeron Kohime|Aeron Kohime]]&lt;br /&gt;
||Causes a child in a link set to become phantom without the entire object becoming phantom.&lt;br /&gt;
|-&lt;br /&gt;
||[[PhysicsLib]]&lt;br /&gt;
||[[User:Nexii Malthus|Nexii Malthus]]&lt;br /&gt;
|| Interesting set of fun physics functions.&lt;br /&gt;
|-&lt;br /&gt;
||[[Play and Loop Sound]]&lt;br /&gt;
||[[User:Bellla Clarity|Bella Clarity]]&lt;br /&gt;
||Very short and simple script; plays and loops a sound in an object.&lt;br /&gt;
|-&lt;br /&gt;
||[[Posing stand|Posing Stand]]&lt;br /&gt;
||[[User:Bellla Clarity|Bella Clarity]]&lt;br /&gt;
||Just a pose script to edit [[attachments]] more easily.&lt;br /&gt;
|-&lt;br /&gt;
||[[PosJump]]&lt;br /&gt;
||[[User:Uchi Desmoulins|Uchi Desmoulins]]&lt;br /&gt;
||A much more efficient alternative to the popular [[warpPos]] function for bypassing 10m distance-moved limitations.&lt;br /&gt;
|-&lt;br /&gt;
||[[Prefix Calculator]]&lt;br /&gt;
||[[User:Xaviar Czervik|Xaviar Czervik]]&lt;br /&gt;
|| A calculator that evaluates expressions in prefix notation. &lt;br /&gt;
&amp;lt;code&amp;gt;+ 3 4 = 5. * + 1 2 + 3 4 = 14.&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||[[Progress Bar]]&lt;br /&gt;
||[[User:Nexii Malthus|Nexii Malthus]]&lt;br /&gt;
|| Flexible and powerful little function for creating progress bars useful in hovertext.&lt;br /&gt;
|-&lt;br /&gt;
||[[Pseudo-random Number Generator]]&lt;br /&gt;
||[[User:Xaviar Czervik|Xaviar Czervik]]&lt;br /&gt;
||Generates a Pseudo-random number between -0x7FFFFFFF and 0x7FFFFFFF&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Rolig_Loon/Quiz_From_Notecard|Quiz From Notecard]]&lt;br /&gt;
||[[User:Rolig Loon|Rolig Loon]]&lt;br /&gt;
||A multiple-choice testing script that reads questions and answer choices from a notecard and presents them in dialog boxes.&lt;br /&gt;
|-&lt;br /&gt;
||[[Rainbow_palette]]&lt;br /&gt;
||[[User:Rui Clary|Rui Clary]]&lt;br /&gt;
||Build a color picker, using only one prim, and a few lines of code.&lt;br /&gt;
|-&lt;br /&gt;
||[[Random AV Profile Projector]]&lt;br /&gt;
||[[User:Debbie Trilling|Debbie Trilling]]&lt;br /&gt;
||Randomly selects an AV from a crowd &amp;amp; then projects their profile picture as a &#039;holographic&#039; image &lt;br /&gt;
|-&lt;br /&gt;
||[[Random Object Vendor]]&lt;br /&gt;
||[[User:CodeBastard Redgrave|CodeBastard Redgrave]]&lt;br /&gt;
||Simple vendor that gives out random objects when paid the right amount &lt;br /&gt;
|-&lt;br /&gt;
||[[Random Password Generator]]&lt;br /&gt;
||[[User:Jor3l Boa|Jor3l Boa]]&lt;br /&gt;
||Generate Random passwords based on String Length.&lt;br /&gt;
|-&lt;br /&gt;
||[[RavText]]&lt;br /&gt;
||[[User:Ravenous Dingo|Ravenous Dingo]]&lt;br /&gt;
||An alternate to XyText.  This is a lightweight, multiple font 10 character text display system.  It only supports uppercase alphanumeric text and a few special characters, but it is very fast, renders quickly and supports multiple fonts. It is meant for specialized use when all that is desired is basic, fast text display and the extra &amp;quot;bells and whistles&amp;quot; are not needed.&lt;br /&gt;
|-&lt;br /&gt;
||[[User:PixelProphet Lane/Scripts#Real Object Inventory To Dialog|Real Object Inventory To Dialog]]&lt;br /&gt;
||[[User:PixelProphet Lane|PixelProphet Lane]]&lt;br /&gt;
||Display any amount of items contained in an Object in a Dialog, regardless of item name length.&lt;br /&gt;
|-&lt;br /&gt;
||[[RentalBoxv1|Rental Box, Simply]]&lt;br /&gt;
||[[User:Sendao Goodman|Sendao Goodman]]&lt;br /&gt;
||Simple explanation of how to make rental boxes.&lt;br /&gt;
|-&lt;br /&gt;
||[[Rental Cube]]&lt;br /&gt;
||[[User:Hank Ramos|Hank Ramos]]&lt;br /&gt;
||Simple cube for renting out a space.&lt;br /&gt;
|-&lt;br /&gt;
||[[Remote Texture Loader]]&lt;br /&gt;
||{{User|Bobbyb30 Swashbuckler}}&lt;br /&gt;
||A set of scripts for remotely loading textures within a sim. This means that a single &amp;quot;texture server&amp;quot; can manage multiple changing billboards within a sim.&lt;br /&gt;
|-&lt;br /&gt;
||[[Say Region Frames Per Second|Region Frames Per Second]]&lt;br /&gt;
||[[User:Heymeriou Mystakidou|Heymariou Mystakidou]]&lt;br /&gt;
|| Says the region name and frames per second out loud on command.&lt;br /&gt;
|-&lt;br /&gt;
||[[sbDialog]]&lt;br /&gt;
||[[User:Siann_Beck|Siann Beck]]&lt;br /&gt;
||A simple replacement function for [[llDialog]]. It re-orders the button list so that the button values, as passed to it, display left-to-right, top-to-bottom. It also opens a [[llListen|listen]] on the specified channel, and returns the handle.&lt;br /&gt;
|-&lt;br /&gt;
||[[Scheduler]]&lt;br /&gt;
||[[User:Haravikk Mistral|Haravikk Mistral]]&lt;br /&gt;
||Schedule multiple events using a single script timer&lt;br /&gt;
|-&lt;br /&gt;
||[[Scheme_Interpreter|Scheme Interpreter]]&lt;br /&gt;
||[[User:Xaviar Czervik|Xaviar Czervik]]&lt;br /&gt;
||A scheme interpreter capable of handling most scheme expressions, including lambda and lists.&lt;br /&gt;
|-&lt;br /&gt;
||[[Script Override Functions]]&lt;br /&gt;
||[[User:Revolution Perenti|Revolution Perenti]]&lt;br /&gt;
||bypass default strings, integer etc in chat channel.&lt;br /&gt;
|-&lt;br /&gt;
||[[Scripted Attachment Detector.lsl|Scripted Attachment Detector]]&lt;br /&gt;
||{{User|Bobbyb30 Swashbuckler}}&lt;br /&gt;
||A script that will display avatars wearing scripted attatchments in hovertext. This may be worn as an hud or rezzed. &lt;br /&gt;
|-&lt;br /&gt;
||[[Self Upgrading Script Enhanced]]&lt;br /&gt;
||[[User:Cron Stardust|Cron Stardust]]&lt;br /&gt;
||Keeps only latest version of the script on prim (even with multiple adds of the same script!)&lt;br /&gt;
|-&lt;br /&gt;
||[[Sensor Visualizer]]&lt;br /&gt;
||[[User:Cerise Sorbet|Cerise Sorbet]]&lt;br /&gt;
||Shows the size and shape you get with [[llSensor]] range and arc parameters&lt;br /&gt;
|-&lt;br /&gt;
||[[Serverless Key Exchange]]&lt;br /&gt;
||[[User:Sendao Goodman|Sendao Goodman]]&lt;br /&gt;
||Maintains a network of object keys without using an external server.&lt;br /&gt;
|-&lt;br /&gt;
||[[SHA1|SHA1 Hash]]&lt;br /&gt;
||[[User:Xaviar Czervik|Xaviar Czervik]]&lt;br /&gt;
||Preforms a SHA1 Hash on an input text. Similar to MD5 only (slightly) more secure. &lt;br /&gt;
|-&lt;br /&gt;
||[[Sim Map Particle Projector]]&lt;br /&gt;
||[[User:Jesse Barnett|Jesse Barnett]]&lt;br /&gt;
||Displays a floating map of the sim the script is in.&lt;br /&gt;
|-&lt;br /&gt;
||[[Sim Restart Logger]]&lt;br /&gt;
||[[User:Kyrah Abattoir|Kyrah Abattoir]]&lt;br /&gt;
||Counts region restarts and displays log of last 9 restarts together with region FPS and dilation. &lt;br /&gt;
|-&lt;br /&gt;
||[[Simple Elevator in a Box]]&lt;br /&gt;
||[[User:Hank Ramos|Hank Ramos]]&lt;br /&gt;
||Simple elevator example.&lt;br /&gt;
|-&lt;br /&gt;
||[[Skillingo AntiHack Script]]&lt;br /&gt;
||[[User:Tmzasz Luminos|Tmzasz Luminos]]&lt;br /&gt;
||A simple Protection script for skillingo thats modifyable to work with other machines.&lt;br /&gt;
|-&lt;br /&gt;
||[[Skunk Money]]&lt;br /&gt;
||[[User:Hank Ramos|Hank Ramos]]&lt;br /&gt;
||Fun gambling machine of yesteryear, which only supports freeplay now due to SL regulations against gambling. &lt;br /&gt;
|-&lt;br /&gt;
||[[SLateIt]]&lt;br /&gt;
||[[User:Babbage Linden|Babbage Linden]]&lt;br /&gt;
||An augmented virtual reality HUD.&lt;br /&gt;
|-&lt;br /&gt;
||[[SLetanque]]&lt;br /&gt;
||[[User:Babbage Linden|Babbage Linden]]&lt;br /&gt;
||An LSL petanque game.&lt;br /&gt;
|-&lt;br /&gt;
||[[SLURL HUD]]&lt;br /&gt;
||[[User:CodeBastard Redgrave|CodeBastard Redgrave]]&lt;br /&gt;
||Touch this HUD to get a SLURL through IM, email and floating text.&lt;br /&gt;
|-&lt;br /&gt;
||[[llSLURL (NewAge)]]&lt;br /&gt;
||[[User:Archile Azalee|Archile Azalee]]&lt;br /&gt;
||Newest most easiest way to create SLURL in a single function llSLURL&lt;br /&gt;
|-&lt;br /&gt;
||[[SL Mail V1.2]]&lt;br /&gt;
||[[User:Flennan Roffo|Flennan Roffo]]&lt;br /&gt;
||Second Life mail client V1.2 (released sept&#039;07). Send and receive mail from within Second Life from and to any address. With Address Book function and many chat commands. V1.3 is upcoming soon! &lt;br /&gt;
|-&lt;br /&gt;
||[[SL_NTPoHTTP_v1.1_client|SL NTPoHTTP client]]&lt;br /&gt;
|[[User:SignpostMarv Martin|SignpostMarv Martin]]&lt;br /&gt;
||Second Life Needs Time Parsing over Hyper Text Transfer Protocol&lt;br /&gt;
Emulates the function of [[llGetWallclock]] for any timezone by using SLOpenID&#039;s SLNTPoHTTP service. Also supports ISO 8601 and RFC 2822 timestamps. Script is dependent upon an external service operated by the author!&lt;br /&gt;
|-&lt;br /&gt;
||[[Smooth Rotating Door]]&lt;br /&gt;
|[[User:Toy Wylie|Toy Wylie]]&lt;br /&gt;
||A script for doors that open and close smoothly using llTargetOmega&lt;br /&gt;
|-&lt;br /&gt;
||[[Smooth Sliding Door]]&lt;br /&gt;
|[[User:SimonT Quinnell|SimonT Quinnell]]&lt;br /&gt;
||A script for sliding doors that open and close smoothly using [[llMoveToTarget]]. Asjusts for prim orientation.&lt;br /&gt;
|-&lt;br /&gt;
||[[Speed Tester]]&lt;br /&gt;
||{{User|Bobbyb30 Swashbuckler}}&lt;br /&gt;
||Similar to the [[Efficiency Tester]], this script allows you to test the speed of a particular function or snippet with multiple trials giving min/max/avg/median and the standard deviation.&lt;br /&gt;
|-&lt;br /&gt;
||[[Spiral Staircase Generator]]&lt;br /&gt;
|[[User:Meyermagic Salome|Meyermagic Salome]]&lt;br /&gt;
||Generates nice looking spiral staircases without much hassle.&lt;br /&gt;
|-&lt;br /&gt;
||[[Static URL&#039;s for HTTP-In Service|Static_URLs]]&lt;br /&gt;
||[[User:Jor3l Boa|Jor3l Boa]]&lt;br /&gt;
||How to generate a static url for HTTP-In temporal limitations.&lt;br /&gt;
|-&lt;br /&gt;
||[[String Compare]]&lt;br /&gt;
||[[User:Xaviar Czervik|Xaviar Czervik]]&lt;br /&gt;
||Compares two strings and reliably returns either 1, -1, or 0 if they are the same.&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Void_Singer/Functions#String:_Find_Last_Index|String: Reverse]]&lt;br /&gt;
||[[User:Void Singer|Void Singer]]&lt;br /&gt;
||Returns an input string in reverse order&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Void_Singer/Functions#String:_Get_Reverse_Order|String: Last Index]]&lt;br /&gt;
||[[User:Void Singer|Void Singer]]&lt;br /&gt;
||Returns the last index of search found in string (the backward equivalent of [[llSubStringIndex]])&lt;br /&gt;
|-&lt;br /&gt;
||[[Tail Messages (NewAge)]]&lt;br /&gt;
||[[User:Asia Snowfall|Asia Snowfall]]&lt;br /&gt;
||A super nice easy to use script for those non-scripters out there! NewAge coding has done it again where you no longer need to scroll up and down adding buttons and adding messages, Features a tag system (you&#039;ll see what i mean :P)&lt;br /&gt;
|-&lt;br /&gt;
||[[Teleport HUD]]&lt;br /&gt;
||[[User:Jesse Barnett|Jesse Barnett]]&lt;br /&gt;
||WORKS TO 4096 METERS!! Very user friendly teleport HUD. Add destinations by touching &amp;quot;Add&amp;quot; &amp;amp; naming destination in chat. Automatically gets sim name and coordinates. Will only display the destinations in the sim you are currently in. Demonstrates more advanced list manipulation and stride functions.&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Void_Singer/Programs#v7-D_Enh._Landmark-2-Map|Teleporter (landmark based)]]&lt;br /&gt;
||[[User:Void_Singer|Void_Singer]]&lt;br /&gt;
||Uses Landmarks to offer map teleports. Works as Hud or inworld objects, just drop in landmarks and go.&lt;br /&gt;
|-&lt;br /&gt;
||[[Text_Scroller|Text Scroller]]&lt;br /&gt;
||[[User:Fred_Gandt|Fred Gandt]]&lt;br /&gt;
||A simple text display object that scrolls text (applied as a texture) from right to left (like &#039;&#039;those&#039;&#039; LED signs) in a continuous loop.&lt;br /&gt;
|-&lt;br /&gt;
||[[Bobbyb&#039;s texture changer|Texture Changers]]&lt;br /&gt;
||{{User|Bobbyb30 Swashbuckler}}&lt;br /&gt;
||A collection of texture changing scripts.&lt;br /&gt;
|-&lt;br /&gt;
||[[Texture Menu Management|Texture Management]]&lt;br /&gt;
||[[User:Revolution Perenti|Revolution Perenti]]&lt;br /&gt;
||Dialog Menu based Texture Selection.&lt;br /&gt;
|-&lt;br /&gt;
||[[Tic Tac Toe]]&lt;br /&gt;
||[[User:CG Linden|CG Linden]]&lt;br /&gt;
||Step by step demo on how to implement a larger scripting project&lt;br /&gt;
|-&lt;br /&gt;
||[[TightList]]&lt;br /&gt;
||[[User:Strife Onizuka|Revolution Perenti]]&lt;br /&gt;
||Tight List is a family of functions for encoding lists as strings and then decoding them back into lists.&lt;br /&gt;
There are two flavors: TightList and TightListType. TightListType preserves types and uses a 6 char header, while TightList uses a 1 char header that doesn&#039;t preserve type. &lt;br /&gt;
|-&lt;br /&gt;
||[[Timer Module]]&lt;br /&gt;
||[[User:Isabelle Aquitaine|Isabelle Aquitaine]]&lt;br /&gt;
||Manage multiple timers via linked messages.&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Void_Singer/Functions#Unix_time_code_to_list_format|Timestamp:&amp;lt;br&amp;gt;Unix time code to list format]]&lt;br /&gt;
||[[User:Void Singer|Void Singer]]&lt;br /&gt;
||converts Unix timestamps to their [Y, M, D, h, m, s] equivalents (ex: 1234567890 to [2009, 2, 13, 3, 31, 30])&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Void_Singer/Functions#List_format_to_Unix_time_code.|Timestamp:&amp;lt;br&amp;gt;List format to Unix time code]]&lt;br /&gt;
||[[User:Void Singer|Void Singer]]&lt;br /&gt;
||converts [Y, M, D, h, m, s] timestamps to their Unix equivalents (ex: [2009, 2, 13, 3, 31, 30] to 1234567890)&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Void_Singer/Functions#Weekday_from_Unix_timestamp|Timestamp:&amp;lt;br&amp;gt;Weekday from Unix timestamp]]&lt;br /&gt;
||[[User:Void Singer|Void Singer]]&lt;br /&gt;
||Gets weekday from Unix timestamps (ex: &amp;quot;Friday&amp;quot; from 1234567890)&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Void_Singer/Functions#Weekday_from_.28_Y.2C_M.2C_D_.29_format|Timestamp:&amp;lt;br&amp;gt;Weekday from (Y, M, D) format]]&lt;br /&gt;
||[[User:Void Singer|Void Singer]]&lt;br /&gt;
||Gets weekday from (Y, M, D) timestamps (ex: &amp;quot;Friday&amp;quot; from (2009, 2, 13))&lt;br /&gt;
|-&lt;br /&gt;
||[[Touch A Quote]]&lt;br /&gt;
||[[User:CodeBastard Redgrave|CodeBastard Redgrave]]&lt;br /&gt;
||Touch an object to read quotes sequentially from a notecard&lt;br /&gt;
|-&lt;br /&gt;
||[[Touring Balloon]]&lt;br /&gt;
||[[User:Hank Ramos|Hank Ramos]]&lt;br /&gt;
||Automated touring balloon with many options from long ago.  Always seems to work on one SL release, and not the other.&lt;br /&gt;
|-&lt;br /&gt;
||[[Under Age Boot]]&lt;br /&gt;
|[[User:Chance Unknown|Chance Unknown]]&lt;br /&gt;
||Security device example to teleport home accounts below a minimum age limit; can be useful in combating free griefer accounts.&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Rolig_Loon/UNDO_PosRot|UNDO_PosRot]]&lt;br /&gt;
||[[User:Rolig Loon|Rolig Loon]]&lt;br /&gt;
||Allows user to undo position and rotation changes that have been made &amp;quot;manually&amp;quot; to any or all prims in a linkset.&lt;br /&gt;
|-&lt;br /&gt;
||[[Universal Translator]]&lt;br /&gt;
|[[User:Hank Ramos|Hank Ramos]]&lt;br /&gt;
||Chat listener that handles seamless translation of public chat between 50+ written languages without the need for configuration.  Handles numerous avatars, auto-detects languages, and works together with multiple copies of translators to spread-workload using a sophisticated back-end communications sub-system.&lt;br /&gt;
|-&lt;br /&gt;
||[[Unix2DateTime]]&lt;br /&gt;
|[[User:Flennan Roffo|Flennan Roffo]]&lt;br /&gt;
||Conversion from Unix time ([[llGetUnixTime]]()) to date and time string and vice versa.&lt;br /&gt;
|-&lt;br /&gt;
||[[Unmutable Descript Nagger]]&lt;br /&gt;
|[[User:Bobbyb30 Zohari|Bobbyb30 Zohari]]&lt;br /&gt;
||To nag avatars to take off their scripted attatchments.&lt;br /&gt;
|-&lt;br /&gt;
||[[Unpacker On Rez (NewAge)]]&lt;br /&gt;
|[[User:Asia Snowfall|Asia Snowfall]]&lt;br /&gt;
||A very simple to use script for all you creators out there, This script will enable you to send out boxed items and make it easier for users to unpack, Also features auto die on completion. Very simple to configure!&lt;br /&gt;
|-&lt;br /&gt;
||[[Unpacker On Touch (NewAge)]]&lt;br /&gt;
|[[User:Asia Snowfall|Asia Snowfall]]&lt;br /&gt;
||Exactly like Unpacker On Rez, But changed some things about to make it Unpack On Touch&lt;br /&gt;
|-&lt;br /&gt;
||[[Update distributor]]&lt;br /&gt;
|[[User:Dale Innis|Dale Innis]]&lt;br /&gt;
||Distribute an object (like a project update) to a list of people named in a notecard.&lt;br /&gt;
|-&lt;br /&gt;
||[[UUID2Channel]]&lt;br /&gt;
||[[User:Project Neox|Project Neox]]&lt;br /&gt;
||Optimised version of the original key2channel generators.&lt;br /&gt;
|-&lt;br /&gt;
||[[UUID Song Generator]]&lt;br /&gt;
||{{User|Sendao Goodman}}&lt;br /&gt;
||Translates a UUID into a simple song and plays it.&lt;br /&gt;
|-&lt;br /&gt;
||[[VirtualID_URLMap|VirtualID URL Mapper for HTTP-in]]&lt;br /&gt;
|[[User:Cenji Neutra|Cenji Neutra]]&lt;br /&gt;
||A script showing how to setup a static URL of the form &amp;lt;your-alias&amp;gt;.obj.virtualid.info which maps to the dynamic HTTP-in URL LSL generates and keeps it up-to-date.&lt;br /&gt;
|-&lt;br /&gt;
||[[User:Void_Singer/Programs#v7-D_Advanced_Visitor_Greeter|Visitor Greeter]]&lt;br /&gt;
|[[User:Void_Singer|Void_Singer]]&lt;br /&gt;
||Reduced spam visitor greeter, highly configurable, easy to modify.&lt;br /&gt;
|-&lt;br /&gt;
||[[Visitor Logger (Web/Basic) ]]&lt;br /&gt;
|[[User:Buddy Sprocket|Buddy Sprocket]]&lt;br /&gt;
||A very basic visitor logger - log visitors in SL to a text file on your web-site.&lt;br /&gt;
|-&lt;br /&gt;
||[[Vote Simple]]&lt;br /&gt;
|[[User:JB_Kraft|JB Kraft]]&lt;br /&gt;
||Simple vote collector. One avi, one vote.&lt;br /&gt;
|-&lt;br /&gt;
||[[Walking Sound (NewAge)]]&lt;br /&gt;
|[[User:Asia Snowfall|Asia Snowfall]]&lt;br /&gt;
||Very powerful walking sound script, Featuring customer ability to add their own sounds with the API Sound Feature!&lt;br /&gt;
|-&lt;br /&gt;
||[[WarpPos]]&lt;br /&gt;
|[[User:Keknehv Psaltery|Keknehv Psaltery]]&lt;br /&gt;
||Non-physical movement without the 10m limit.&lt;br /&gt;
|-&lt;br /&gt;
||[[Wiki3DBuilder]] [[Wiki3DBuilder1.0]]&lt;br /&gt;
||[[User:Salahzar Stenvaag|Salahzar Stenvaag]] &lt;br /&gt;
||Allows a group of people to collectively build up complex 3D mindmaps with connected concepts. Uses particles for connections and low prim usage. Nodes can be textured, colored, changed form size moved collectively by everybody and can distribute notecards, landmarks, URL, objects, textures. I provide two version 0.1 and 1.0 and probably next version will communicate with moodle and sloodle external websites.&lt;br /&gt;
|-&lt;br /&gt;
||[[Window Control]]&lt;br /&gt;
||[[User:Emmas Seetan|Emmas Seetan]]&lt;br /&gt;
||For window opacity, helpful for buildings.&lt;br /&gt;
|-&lt;br /&gt;
||[[WHMcs SecondLife plugin]]&lt;br /&gt;
|[[User:Alicia Sautereau|Alicia Sautereau]]&lt;br /&gt;
||Linden Dollar payment plugin for the WHMcs hosting portal.&lt;br /&gt;
|-&lt;br /&gt;
||[[XTEA Strong Encryption Implementation]]&lt;br /&gt;
||[[User:Morse Dillon|Morse Dillon]]&lt;br /&gt;
||An LSL implementation of XTEA (eXtended Tiny Encryption Algorithm).  This is the first known public release of a &#039;real&#039; strong encryption implementation in LSL and is released under the GNU General Public License (GPL).&lt;br /&gt;
|-&lt;br /&gt;
||[[XyText 1.5|XyText]]&lt;br /&gt;
||[[User:Xylor Baysklef|Xylor Baysklef]]&lt;br /&gt;
|| Display text (up to 10 characters) on a prim. Use as many prims as desired.&lt;br /&gt;
|-&lt;br /&gt;
||[[XyyyyzText|XyyyyzText]]&lt;br /&gt;
||[[User:Criz Collins|Criz Collins]]&lt;br /&gt;
|| Display text (up to 10 characters) on a prim. Displays different text for each line instead of one single text, that will be broken into the next lines. Watch here for what that means: http://screencast.com/t/1wMLujLcEO&lt;br /&gt;
|-&lt;br /&gt;
||[[XyzzyText|XyzzyText]]&lt;br /&gt;
||[[User:Thraxis Epsilon|Thraxis Epsilon]] and [[User:Gigs Taggart|Gigs Taggart]]&lt;br /&gt;
|| Display text (up to 10 characters) on a prim. Way more efficient than XyText.&lt;br /&gt;
|-&lt;br /&gt;
||[[Youtube TV]]&lt;br /&gt;
||[[User:Morgam Biedermann|Morgam Biedermann]]&lt;br /&gt;
||Watch your favorite Youtube videos / auto set the texture to the parcel media texture.&lt;br /&gt;
|-&lt;br /&gt;
||[[Zero Lag Poseball]]&lt;br /&gt;
||[[User:Jippen Faddoul|Jippen Faddoul]] and [[User:Daemonika Nightfire|Daemonika Nightfire]]&lt;br /&gt;
||A simple poseball with no lag&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;box&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Support Script Library==&lt;br /&gt;
These are scripts in other languages, intended to be run on other systems that support scripts written in LSL&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;sortable&amp;quot; {{Prettytable}}&lt;br /&gt;
|- {{Hl2}}&lt;br /&gt;
! &#039;&#039;&#039;Name&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Creator&#039;&#039;&#039;&lt;br /&gt;
! &#039;&#039;&#039;Description&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|| [[User:Ina Centaur/UUID/calimg.api|Calendar Image UUID API]]&lt;br /&gt;
|| [[User:Ina Centaur|Ina Centaur]]&lt;br /&gt;
|| API that gives the UUID of an image of a calendar image give month and year arguments.&lt;br /&gt;
|-&lt;br /&gt;
||[[HTTP Post request to a PHP server]]&lt;br /&gt;
||[[User:Corto Maltese|Corto Maltese]]&lt;br /&gt;
|| This small library allows you to make simple POST requests to your website. The libraries allow you to get your request through the variable $_POST on the server. It also include a basic security mechanism aimed to stop hacking. Comprises of LSL client script and PHP server script.&lt;br /&gt;
|-&lt;br /&gt;
|| [[User:Ina Centaur/PHP/k2n.php|Key2Name.php]]&lt;br /&gt;
|| [[User:Ina Centaur|Ina Centaur]]&lt;br /&gt;
|| Get Avatar&#039;s Name using Second Life search service. (like in LSL Key2Name)&lt;br /&gt;
|-&lt;br /&gt;
|| [[Lame_Object_DNS_and_Cross_Sim_Messaging|Lame Object DNS and Cross Sim Messaging]]&lt;br /&gt;
|| [[User:Liandra Ceawlin|Liandra Ceawlin]]&lt;br /&gt;
|| Simple, cheeseball method of doing cross-sim communications with http-in and an external object DNS service.&lt;br /&gt;
|-&lt;br /&gt;
|| [[User:SignpostMarv Martin/PHP/llXorBase64StringsCorrect|llXorBase64StringsCorrect]]&lt;br /&gt;
|| [[User:SignpostMarv Martin|SignpostMarv Martin]]&lt;br /&gt;
|| An implementation of [[llXorBase64StringsCorrect]] in PHP- should be useful if you&#039;re using llXorBase64StringsCorrect to do cryptography work in LSL2 and posting it out to the web via [[llHTTPRequest]].&lt;br /&gt;
|-&lt;br /&gt;
|| [[User:SignpostMarv Martin/PHP/lsl fu.php|lsl_fu.php]]&lt;br /&gt;
|| [[User:SignpostMarv Martin|SignpostMarv Martin]]&lt;br /&gt;
|| A basic OOP&#039;d PHP Class containing VeloxSeverine&#039;s $_POST fixer and Marv&#039;s own eccentric ideas for &amp;quot;fixing&amp;quot; things.&lt;br /&gt;
|-&lt;br /&gt;
|| [[User:Jor3l Boa/PHP/n2k.php|Name2Key.php]]&lt;br /&gt;
|| [[User:Jor3l Boa|Jor3l Boa]]&lt;br /&gt;
|| Get Avatar&#039;s UUID using Second Life search service. (like in LSL Name2Key)&lt;br /&gt;
|-&lt;br /&gt;
|| [[Public_Object_DNS|Public Object DNS]]&lt;br /&gt;
|| [[User:Liandra Ceawlin|Liandra Ceawlin]]&lt;br /&gt;
|| Public object DNS-like system running on GAE, for http-in. Hopefully scalable enough for wide-spread usage.&lt;br /&gt;
|-&lt;br /&gt;
|| Silo&lt;br /&gt;
|| [[User:Zero_Linden|Zero Linden]]&lt;br /&gt;
|| General purpose data store in PHP.  Use this to persist arbitrary data from LSL via [[llHTTPRequest]].  See:&lt;br /&gt;
* [http://www.notabene-sl.com/misc/silo-README.txt README] file&lt;br /&gt;
* [http://www.notabene-sl.com/misc/silo.tgz silo.tgz] tarball&lt;br /&gt;
* forum post [http://forums-archive.secondlife.com/54/69/119570/1.html Announcement].&lt;br /&gt;
|-&lt;br /&gt;
||[[Silverday ObjectDNS]]&lt;br /&gt;
||[[User:Till Stirling|Till Stirling]]&lt;br /&gt;
||Dynamic Object-DNS-System to provide dynamic mapping of LSL-URLs to persistent domains. Features include redirect service, password protected domains, write protected domains, LSL-API for all necessary functions, optional web-interface.&lt;br /&gt;
|-&lt;br /&gt;
|| [http://aubretec.com/products/sldb SLDB]&lt;br /&gt;
|| [[User:Luc Aubret|Luc Aubret]]&lt;br /&gt;
|| Flexible web database storage using PHP/MySQL.  Used to store per-user field/value pairs from in-world objects using [[llHTTPRequest]]. &lt;br /&gt;
* [http://aubretec.com/support/manuals/sldbkit/ Implementation Guide]&lt;br /&gt;
* [http://aubretec.com/wp-content/uploads/2009/05/sldb.zip Download] (.zip, 12kb)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== See Also ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 0.5em&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Articles&#039;&#039;&#039;&lt;br /&gt;
*[[:Category:LSL Examples| Examples]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{LSLC|}}&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1021582</id>
		<title>MultiUser Dialog Handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=MultiUser_Dialog_Handler&amp;diff=1021582"/>
		<updated>2010-09-01T05:05:34Z</updated>

		<summary type="html">&lt;p&gt;SimonT Quinnell: /* Calling a Complex Menu */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
I found that i was constantly rewriting a pile of dialog handling functions in my scrips so i decided to write a single dialog handler script. It needed to have the following features;&lt;br /&gt;
&lt;br /&gt;
* Support multiple simultaneous menus from different users.&lt;br /&gt;
* Support fixed top buttons that will persist on lists.&lt;br /&gt;
* Support fixed bottom buttons with include Back and Exit buttons.&lt;br /&gt;
* Handle item lists &amp;gt; 12 and automatically insert &amp;lt;&amp;lt; and  &amp;gt;&amp;gt; buttons to scroll through the lists.&lt;br /&gt;
* Handle timeouts and communicate timeout back to calling script.&lt;br /&gt;
* Handles truncation of the buttons and text to stop errors in llDialog&lt;br /&gt;
&lt;br /&gt;
==Using The Script==&lt;br /&gt;
&lt;br /&gt;
===Calling a Simple Menu===&lt;br /&gt;
In the script that is calling the menu I use a function something like this.  This will display a menu that looks something like&lt;br /&gt;
&lt;br /&gt;
 This is the menu text that will be displayed.&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|Owner || Group || All&lt;br /&gt;
|-&lt;br /&gt;
|      || Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;menu~parent|TRUE&amp;quot;;  //Menu name is &#039;menu with a parent menu name &#039;parent&#039;.  The display of the bottom navigation buttons is set to TRUE&lt;br /&gt;
    string  menuText = &amp;quot;This is the menu text that will be displayed.&amp;quot;;  // The text ( will be trimmed to 510 characters&lt;br /&gt;
    string  menuButtons =  &amp;quot;Owner~Group~All&amp;quot;;                            // The buttons, each button separated by a &#039;~&#039;.  Buttons will be trimmed to 24 characters&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Calling a Complex Menu===&lt;br /&gt;
The above case is a simple menu.  The script can also do more complex menus.  &lt;br /&gt;
&lt;br /&gt;
In the following case we will have 10 buttons that are going to be displayed.  Because we will also display the bottom 3 buttons this will be spanned across 2 menus.  We will also include 2 fixed buttons that always display at the top of the menu, PLUS each button has a unique descriptor that is displayed.&lt;br /&gt;
&lt;br /&gt;
This will display the following 2 menus&lt;br /&gt;
&lt;br /&gt;
Menu1&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item1&lt;br /&gt;
 item2&lt;br /&gt;
 item3&lt;br /&gt;
 item4&lt;br /&gt;
 item5&lt;br /&gt;
 item6&lt;br /&gt;
 item7&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B1&lt;br /&gt;
|-&lt;br /&gt;
|B2     ||   B3   || B4&lt;br /&gt;
|-&lt;br /&gt;
|B5     ||   B6   || B7&lt;br /&gt;
|-&lt;br /&gt;
|       ||  Back  || &amp;gt;&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Menu2 (on clicking the &amp;gt;&amp;gt; button)&lt;br /&gt;
 Main text displayed.&lt;br /&gt;
 item8&lt;br /&gt;
 item9&lt;br /&gt;
 item10&lt;br /&gt;
 &lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|FIXED1 || FIXED2 || B8&lt;br /&gt;
|-&lt;br /&gt;
| B9     ||   B10  || &lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;&amp;lt;     ||  Back  || Exit&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id)&lt;br /&gt;
{   // Menu Functions&lt;br /&gt;
    string  menuDescripter = &amp;quot;menu~parent|TRUE&amp;quot;;&lt;br /&gt;
    string  menuText = &amp;quot;Main text displayed.~text1~text2~text3~text4~text5~text6~text7~text8~text9~text10&amp;quot;; &lt;br /&gt;
    string  menuButtons =  &amp;quot;B1~B2~B3~B4~B5~B6~B7~B8~B9~B10&amp;quot;;                            &lt;br /&gt;
    string  menuFixedButtons = &amp;quot;FIXED1~FIXED2&amp;quot;&lt;br /&gt;
        &lt;br /&gt;
    llMessageLinked(LINK_THIS, LINK_MENU_DISPLAY, menuDescripter+&amp;quot;|&amp;quot;+menuText+&amp;quot;|&amp;quot;+ menuButtons+&amp;quot;|&amp;quot;+menuFixedButtons, id);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Parsing the returning Menu information===&lt;br /&gt;
&lt;br /&gt;
Note that if the user clicks on the &amp;lt;&amp;lt; or &amp;gt;&amp;gt; buttons the calling script WILL NOT see any response.  The correct menu is displayed.  The calling script only receives a response when one of the buttons or Back/Exit are clicked.  Clicking on a space button will cause the menu to redisplay.&lt;br /&gt;
&lt;br /&gt;
The script that expects the response back from the menu has the following&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    link_message(integer intSenderNum, integer num, string message, key id) &lt;br /&gt;
    { &lt;br /&gt;
        if (num == LINK_MENU_RETURN)&lt;br /&gt;
        {&lt;br /&gt;
            list    returnMenu = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
            list    temp = llParseString2List(llList2String(returnMenu,0), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
            string  menu = llList2String(temp,0);             // The name of the menu    &lt;br /&gt;
            string  parent = llList2String(temp,1);           // The parent of the menu&lt;br /&gt;
            string  item = llList2String(returnMenu,1);       // The actual button pushed&lt;br /&gt;
            &lt;br /&gt;
            //now do something with your response which is the variable &#039;item&#039;&lt;br /&gt;
         }&lt;br /&gt;
         else if (num == LINK_MENU_TIMEOUT)&lt;br /&gt;
         {&lt;br /&gt;
             // We received a timeout (don&#039;t worry the listens are all dealt with) so you might want to IM the menu user.&lt;br /&gt;
         }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Script==&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
//&lt;br /&gt;
// Menu Display Script &lt;br /&gt;
//&lt;br /&gt;
// Menu command format&lt;br /&gt;
// string = menuname ~ menuparent | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3  optional}&lt;br /&gt;
// key = menuuser key&lt;br /&gt;
//&lt;br /&gt;
//&lt;br /&gt;
// menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
// menusActive      [menuchannel, menuhandle, time, page] &lt;br /&gt;
//&lt;br /&gt;
// by SimonT Quinnell &lt;br /&gt;
//&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// CONSTANTS&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
// Link Commands&lt;br /&gt;
integer     LINK_MENU_DISPLAY = 300;&lt;br /&gt;
integer     LINK_MENU_CLOSE = 310; &lt;br /&gt;
integer     LINK_MENU_RETURN = 320;&lt;br /&gt;
integer     LINK_MENU_TIMEOUT = 330;&lt;br /&gt;
&lt;br /&gt;
// Main Menu Details&lt;br /&gt;
string      BACK = &amp;quot;&amp;lt;&amp;lt;&amp;quot;;&lt;br /&gt;
string      FOWARD = &amp;quot;&amp;gt;&amp;gt;&amp;quot;;&lt;br /&gt;
list        MENU_NAVIGATE_BUTTONS = [ &amp;quot; &amp;quot;, &amp;quot;Back&amp;quot;, &amp;quot;Exit&amp;quot;];&lt;br /&gt;
float       MENU_TIMEOUT_CHECK = 10.0;&lt;br /&gt;
integer     MENU_TIMEOUT = 120;&lt;br /&gt;
integer     MAX_TEXT = 510;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
integer     STRIDE_DESCRIPTION = 8;&lt;br /&gt;
integer     STRIDE_ACTIVE = 4;&lt;br /&gt;
integer     DEBUG = FALSE;&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Variables&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
list    menusDescription;&lt;br /&gt;
list    menusActive;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - General&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
                   &lt;br /&gt;
debug(string debug)&lt;br /&gt;
{&lt;br /&gt;
    if (DEBUG) llWhisper(0,&amp;quot;DEBUG:&amp;quot;+llGetScriptName()+&amp;quot;:&amp;quot;+debug+&amp;quot; : Free(&amp;quot;+(string)llGetFreeMemory()+&amp;quot;)&amp;quot;);&lt;br /&gt;
}  &lt;br /&gt;
                &lt;br /&gt;
               &lt;br /&gt;
integer string2Bool (string test)&lt;br /&gt;
{&lt;br /&gt;
    if (test == &amp;quot;TRUE&amp;quot;) return TRUE;&lt;br /&gt;
    else return FALSE;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Helpers&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
integer NewChannel()&lt;br /&gt;
{    // generates unique channel number&lt;br /&gt;
    integer channel;&lt;br /&gt;
&lt;br /&gt;
    do channel = -(llRound(llFrand(999999)) + 99999);&lt;br /&gt;
    while (~llListFindList(menusDescription, [channel]));&lt;br /&gt;
&lt;br /&gt;
    return channel;    &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
string  CheckTitleLength(string title)&lt;br /&gt;
{&lt;br /&gt;
    if (llStringLength(title) &amp;gt; MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1);&lt;br /&gt;
    &lt;br /&gt;
    return title;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
list FillMenu(list buttons)&lt;br /&gt;
{   //adds empty buttons until the list length is multiple of 3, to max of 12&lt;br /&gt;
    integer i;&lt;br /&gt;
    list    listButtons;&lt;br /&gt;
    &lt;br /&gt;
    for(i=0;i&amp;lt;llGetListLength(buttons);i++)&lt;br /&gt;
    {&lt;br /&gt;
        string name = llList2String(buttons,i);&lt;br /&gt;
        if (llStringLength(name) &amp;gt; 24) name = llGetSubString(name, 0, 23);&lt;br /&gt;
        listButtons = listButtons + [name];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    while (llGetListLength(listButtons) != 3 &amp;amp;&amp;amp; llGetListLength(listButtons) != 6 &amp;amp;&amp;amp; llGetListLength(listButtons) != 9 &amp;amp;&amp;amp; llGetListLength(listButtons) &amp;lt; 12)&lt;br /&gt;
    {&lt;br /&gt;
        listButtons = listButtons + [&amp;quot; &amp;quot;];&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    buttons = llList2List(listButtons, 9, 11);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 6, 8);&lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 3, 5);    &lt;br /&gt;
    buttons = buttons + llList2List(listButtons, 0, 2); &lt;br /&gt;
    &lt;br /&gt;
    return buttons;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveUser(key id)&lt;br /&gt;
{&lt;br /&gt;
    integer index_id = llListFindList(menusDescription, [id]);&lt;br /&gt;
    &lt;br /&gt;
    while (~index_id) &lt;br /&gt;
    {&lt;br /&gt;
        integer channel = llList2Integer(menusDescription, index_id-1);&lt;br /&gt;
        RemoveMenu(channel, FALSE);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveMenu(integer channel, integer echo)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {&lt;br /&gt;
        key id = llList2Key(menusDescription, index+1);&lt;br /&gt;
        menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION -1);&lt;br /&gt;
        RemoveListen(channel);&lt;br /&gt;
&lt;br /&gt;
        if (echo) llMessageLinked(LINK_THIS, LINK_MENU_TIMEOUT, &amp;quot;Timeout&amp;quot;, id);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
RemoveListen(integer channel)&lt;br /&gt;
{&lt;br /&gt;
    integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
    if (index != -1)&lt;br /&gt;
    {    &lt;br /&gt;
        llListenRemove(llList2Integer(menusActive, index + 1));&lt;br /&gt;
        menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Functions - Menu Main&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
&lt;br /&gt;
NewMenu(string message, integer senderNum, key id)&lt;br /&gt;
{   // Setup New Menu&lt;br /&gt;
    // menusDescription [menuchannel, key, menu &amp;amp; parent, return link, nav?,  titles, buttons, fixed buttons]&lt;br /&gt;
&lt;br /&gt;
    list    temp = llParseString2List(message, [&amp;quot;|&amp;quot;], []);&lt;br /&gt;
    integer channel = NewChannel();&lt;br /&gt;
        &lt;br /&gt;
    if (llGetListLength(temp) &amp;gt; 2)&lt;br /&gt;
    {&lt;br /&gt;
        menusDescription = [channel, id, llList2String(temp, 0), senderNum,  string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;&lt;br /&gt;
&lt;br /&gt;
        DisplayMenu(id, channel, 0);&lt;br /&gt;
    }&lt;br /&gt;
    else llOwnerSay (&amp;quot;ERROR: Dialog Script. Incorrect menu format&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
DisplayMenu(key id, integer channel, integer page)&lt;br /&gt;
{&lt;br /&gt;
    string  menuTitle;&lt;br /&gt;
    list    menuSubTitles;&lt;br /&gt;
    list    menuButtonsAll;&lt;br /&gt;
    list    menuButtons;   &lt;br /&gt;
    list    menuNavigateButtons;&lt;br /&gt;
    list    menuFixedButtons;&lt;br /&gt;
&lt;br /&gt;
    integer max = 12;&lt;br /&gt;
&lt;br /&gt;
    // Populate values&lt;br /&gt;
    integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
    menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    if (llList2String(menusDescription, index+7) != &amp;quot;&amp;quot;) menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
&lt;br /&gt;
    // Set up the menu buttons&lt;br /&gt;
    if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS;&lt;br /&gt;
    else if (llGetListLength(menuButtonsAll) &amp;gt; (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [&amp;quot; &amp;quot;, &amp;quot; &amp;quot;, &amp;quot; &amp;quot;];&lt;br /&gt;
    &lt;br /&gt;
    // FIXME: add sanity check for menu page&lt;br /&gt;
    &lt;br /&gt;
    max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons);&lt;br /&gt;
    integer     start = page*max;&lt;br /&gt;
    integer     stop = (page+1)*max - 1;&lt;br /&gt;
    menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));&lt;br /&gt;
&lt;br /&gt;
    // Generate the title&lt;br /&gt;
    list tempTitle = llParseString2List(llList2String(menusDescription, index+5), [&amp;quot;~&amp;quot;], []);&lt;br /&gt;
    menuTitle = llList2String(tempTitle,0);&lt;br /&gt;
    if (llGetListLength(tempTitle) &amp;gt; 1) menuSubTitles = llList2List(tempTitle, 1, -1);&lt;br /&gt;
    if (llGetListLength(menuSubTitles) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
        integer i;&lt;br /&gt;
        for(i=start;i&amp;lt;(stop+1);++i)&lt;br /&gt;
        {&lt;br /&gt;
            if (llList2String(menuSubTitles, i) != &amp;quot;&amp;quot;) menuTitle += &amp;quot;\n&amp;quot;+llList2String(menuSubTitles, i);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    menuTitle = CheckTitleLength(menuTitle);&lt;br /&gt;
    &lt;br /&gt;
    // Add navigate buttons if necessary&lt;br /&gt;
    if (page &amp;gt; 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0);&lt;br /&gt;
    if (llGetListLength(menuButtonsAll) &amp;gt; (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); &lt;br /&gt;
    &lt;br /&gt;
    // Set up listen and add the row details&lt;br /&gt;
    integer menuHandle = llListen(channel, &amp;quot;&amp;quot;, id, &amp;quot;&amp;quot;);&lt;br /&gt;
    menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;&lt;br /&gt;
&lt;br /&gt;
    llSetTimerEvent(MENU_TIMEOUT_CHECK);&lt;br /&gt;
&lt;br /&gt;
    // Display menu&lt;br /&gt;
    llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
&lt;br /&gt;
// ********************************************************************&lt;br /&gt;
// Event Handlers&lt;br /&gt;
// ********************************************************************  &lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    listen(integer channel, string name, key id, string message)&lt;br /&gt;
    {&lt;br /&gt;
        if (message == BACK) &lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)-1;&lt;br /&gt;
            RemoveListen(channel);            &lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == FOWARD)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3)+1;&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else if (message == &amp;quot; &amp;quot;)&lt;br /&gt;
        { &lt;br /&gt;
            integer index = llListFindList(menusActive, [channel]);&lt;br /&gt;
            integer page = llList2Integer(menusActive, index+3);&lt;br /&gt;
            RemoveListen(channel);&lt;br /&gt;
            DisplayMenu(id, channel, page);&lt;br /&gt;
        }&lt;br /&gt;
        else &lt;br /&gt;
        {&lt;br /&gt;
            integer index = llListFindList(menusDescription, [channel]);&lt;br /&gt;
            llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+&amp;quot;|&amp;quot;+message, id);&lt;br /&gt;
            RemoveMenu(channel, FALSE);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    link_message(integer senderNum, integer num, string message, key id) &lt;br /&gt;
    {&lt;br /&gt;
        if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id);&lt;br /&gt;
        else if (num == LINK_MENU_CLOSE) RemoveUser(id);&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    timer()&lt;br /&gt;
    {   // Check through timers and close if necessary&lt;br /&gt;
        integer i;&lt;br /&gt;
        list toRemove;&lt;br /&gt;
        integer currentTime = llGetUnixTime();&lt;br /&gt;
        integer length = llGetListLength(menusActive);   &lt;br /&gt;
    &lt;br /&gt;
        for(i=0;i&amp;lt;length;i+=STRIDE_ACTIVE)&lt;br /&gt;
        {&lt;br /&gt;
            if (currentTime - llList2Integer(menusActive, i+2) &amp;gt; MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        length = llGetListLength(toRemove);&lt;br /&gt;
        if (length &amp;gt; 0)&lt;br /&gt;
        {&lt;br /&gt;
            for(i=0;i&amp;lt;length;i++)&lt;br /&gt;
            {&lt;br /&gt;
                RemoveMenu(llList2Integer(toRemove, i), TRUE);&lt;br /&gt;
            }&lt;br /&gt;
        }        &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[ Script_Library | Script Library ]]&lt;/div&gt;</summary>
		<author><name>SimonT Quinnell</name></author>
	</entry>
</feed>