Difference between revisions of "MultiUser Dialog Handler"
Line 14: | Line 14: | ||
==The Script== | ==The Script== | ||
<lsl> | <lsl> | ||
// ******************************************************************** | |||
// | |||
// Menu Display Script | |||
// | |||
// Menu command format | |||
// string = menuname ~ menuparent | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3 optional} | |||
// key = menuuser key | |||
// | |||
// | |||
// menusDescription [menuchannel, key, menu & parent, return link, nav?, titles, buttons, fixed buttons] | |||
// menusActive [menuchannel, menuhandle, time, page] | |||
// | |||
// by SimonT Quinnell | |||
// | |||
// ******************************************************************** | |||
// ******************************************************************** | |||
// CONSTANTS | |||
// ******************************************************************** | |||
// Link Commands | |||
integer LINK_MENU_DISPLAY = 300; | |||
integer LINK_MENU_CLOSE = 310; | |||
integer LINK_MENU_RETURN = 320; | |||
integer LINK_MENU_TIMEOUT = 330; | |||
// Main Menu Details | |||
string BACK = "<<"; | |||
string FOWARD = ">>"; | |||
list MENU_NAVIGATE_BUTTONS = [ " ", "Back", "Exit"]; | |||
float MENU_TIMEOUT_CHECK = 10.0; | |||
integer MENU_TIMEOUT = 120; | |||
integer MAX_TEXT = 510; | |||
integer STRIDE_DESCRIPTION = 8; | |||
integer STRIDE_ACTIVE = 4; | |||
integer DEBUG = FALSE; | |||
// ******************************************************************** | |||
// Variables | |||
// ******************************************************************** | |||
list menusDescription; | |||
list menusActive; | |||
// ******************************************************************** | |||
// Functions - General | |||
// ******************************************************************** | |||
debug(string debug) | |||
{ | |||
if (DEBUG) llWhisper(0,"DEBUG:"+llGetScriptName()+":"+debug+" : Free("+(string)llGetFreeMemory()+")"); | |||
} | |||
integer string2Bool (string test) | |||
{ | |||
if (test == "TRUE") return TRUE; | |||
else return FALSE; | |||
} | |||
// ******************************************************************** | |||
// Functions - Menu Helpers | |||
// ******************************************************************** | |||
integer NewChannel() | |||
{ // generates unique channel number | |||
integer channel; | |||
do channel = -(llRound(llFrand(999999)) + 99999); | |||
while (~llListFindList(menusDescription, [channel])); | |||
return channel; | |||
} | |||
string CheckTitleLength(string title) | |||
{ | |||
if (llStringLength(title) > MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1); | |||
return title; | |||
} | |||
list FillMenu(list buttons) | |||
{ //adds empty buttons until the list length is multiple of 3, to max of 12 | |||
integer i; | |||
list listButtons; | |||
for(i=0;i<llGetListLength(buttons);i++) | |||
{ | |||
string name = llList2String(buttons,i); | |||
if (llStringLength(name) > 24) name = llGetSubString(name, 0, 23); | |||
listButtons = listButtons + [name]; | |||
} | |||
while (llGetListLength(listButtons) != 3 && llGetListLength(listButtons) != 6 && llGetListLength(listButtons) != 9 && llGetListLength(listButtons) < 12) | |||
{ | |||
listButtons = listButtons + [" "]; | |||
} | |||
buttons = llList2List(listButtons, 9, 11); | |||
buttons = buttons + llList2List(listButtons, 6, 8); | |||
buttons = buttons + llList2List(listButtons, 3, 5); | |||
buttons = buttons + llList2List(listButtons, 0, 2); | |||
return buttons; | |||
} | |||
RemoveUser(key id) | |||
{ | |||
integer index_id = llListFindList(menusDescription, [id]); | |||
while (~index_id) | |||
{ | |||
integer channel = llList2Integer(menusDescription, index_id-1); | |||
RemoveMenu(channel, FALSE); | |||
} | |||
} | |||
RemoveMenu(integer channel, integer echo) | |||
{ | |||
integer index = llListFindList(menusDescription, [channel]); | |||
if (index != -1) | |||
{ | |||
key id = llList2Key(menusDescription, index+1); | |||
menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION -1); | |||
RemoveListen(channel); | |||
if (echo) llMessageLinked(LINK_THIS, LINK_MENU_TIMEOUT, "Timeout", id); | |||
} | |||
} | |||
RemoveListen(integer channel) | |||
{ | |||
integer index = llListFindList(menusActive, [channel]); | |||
if (index != -1) | |||
{ | |||
llListenRemove(llList2Integer(menusActive, index + 1)); | |||
menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1); | |||
} | |||
} | |||
// ******************************************************************** | |||
// Functions - Menu Main | |||
// ******************************************************************** | |||
NewMenu(string message, integer senderNum, key id) | |||
{ // Setup New Menu | |||
// menusDescription [menuchannel, key, menu & parent, return link, nav?, titles, buttons, fixed buttons] | |||
list temp = llParseString2List(message, ["|"], []); | |||
integer channel = NewChannel(); | |||
if (llGetListLength(temp) > 2) | |||
{ | |||
menusDescription = [channel, id, llList2String(temp, 0), senderNum, string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription; | |||
DisplayMenu(id, channel, 0); | |||
} | |||
else llOwnerSay ("ERROR: Dialog Script. Incorrect menu format"); | |||
} | |||
DisplayMenu(key id, integer channel, integer page) | |||
{ | |||
string menuTitle; | |||
list menuSubTitles; | |||
list menuButtonsAll; | |||
list menuButtons; | |||
list menuNavigateButtons; | |||
list menuFixedButtons; | |||
integer max = 12; | |||
// Populate values | |||
integer index = llListFindList(menusDescription, [channel]); | |||
menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), ["~"], []); | |||
if (llList2String(menusDescription, index+7) != "") menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), ["~"], []); | |||
// Set up the menu buttons | |||
if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS; | |||
else if (llGetListLength(menuButtonsAll) > (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [" ", " ", " "]; | |||
// FIXME: add sanity check for menu page | |||
max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons); | |||
integer start = page*max; | |||
integer stop = (page+1)*max - 1; | |||
menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop)); | |||
// Generate the title | |||
list tempTitle = llParseString2List(llList2String(menusDescription, index+5), ["~"], []); | |||
menuTitle = llList2String(tempTitle,0); | |||
if (llGetListLength(tempTitle) > 1) menuSubTitles = llList2List(tempTitle, 1, -1); | |||
if (llGetListLength(menuSubTitles) > 0) | |||
{ | |||
integer i; | |||
for(i=start;i<(stop+1);++i) | |||
{ | |||
if (llList2String(menuSubTitles, i) != "") menuTitle += "\n"+llList2String(menuSubTitles, i); | |||
} | |||
} | |||
menuTitle = CheckTitleLength(menuTitle); | |||
// Add navigate buttons if necessary | |||
if (page > 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0); | |||
if (llGetListLength(menuButtonsAll) > (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); | |||
// Set up listen and add the row details | |||
integer menuHandle = llListen(channel, "", id, ""); | |||
menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive; | |||
llSetTimerEvent(MENU_TIMEOUT_CHECK); | |||
// Display menu | |||
llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel); | |||
} | |||
// ******************************************************************** | |||
// Event Handlers | |||
// ******************************************************************** | |||
default | |||
{ | |||
listen(integer channel, string name, key id, string message) | |||
{ | |||
if (message == BACK) | |||
{ | |||
integer index = llListFindList(menusActive, [channel]); | |||
integer page = llList2Integer(menusActive, index+3)-1; | |||
RemoveListen(channel); | |||
DisplayMenu(id, channel, page); | |||
} | |||
else if (message == FOWARD) | |||
{ | |||
integer index = llListFindList(menusActive, [channel]); | |||
integer page = llList2Integer(menusActive, index+3)+1; | |||
RemoveListen(channel); | |||
DisplayMenu(id, channel, page); | |||
} | |||
else if (message == " ") | |||
{ | |||
integer index = llListFindList(menusActive, [channel]); | |||
integer page = llList2Integer(menusActive, index+3); | |||
RemoveListen(channel); | |||
DisplayMenu(id, channel, page); | |||
} | |||
else | |||
{ | |||
integer index = llListFindList(menusDescription, [channel]); | |||
llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+"|"+message, id); | |||
RemoveMenu(channel, FALSE); | |||
} | |||
} | |||
link_message(integer senderNum, integer num, string message, key id) | |||
{ | |||
if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id); | |||
else if (num == LINK_MENU_CLOSE) RemoveUser(id); | |||
} | |||
timer() | |||
{ // Check through timers and close if necessary | |||
integer i; | |||
list toRemove; | |||
integer currentTime = llGetUnixTime(); | |||
integer length = llGetListLength(menusActive); | |||
for(i=0;i<length;i+=STRIDE_ACTIVE) | |||
{ | |||
if (currentTime - llList2Integer(menusActive, i+2) > MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove; | |||
} | |||
length = llGetListLength(toRemove); | |||
if (length > 0) | |||
{ | |||
for(i=0;i<length;i++) | |||
{ | |||
RemoveMenu(llList2Integer(toRemove, i), TRUE); | |||
} | |||
} | |||
} | |||
} | |||
</lsl> | </lsl> |
Revision as of 20:53, 31 August 2010
Introduction
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;
- Support multiple simultaneous menus from different users.
- Support fixed top buttons that will persist on lists.
- Support fixed bottom buttons with include Back and Exit buttons.
- Handle item lists > 12 and automatically insert << and >> buttons to scroll through the lists.
- Handle timeouts and communicate timeout back to calling script.
Using The Script
The Script
<lsl> // ******************************************************************** // // Menu Display Script // // Menu command format // string = menuname ~ menuparent | display navigate? TRUE/FALSE | menuMaintitle~subtitle1~subtitle2~subtitle3 | button1~button2~button3 {| fixedbutton1~fixedbutton2~fixedbutton3 optional} // key = menuuser key // // // menusDescription [menuchannel, key, menu & parent, return link, nav?, titles, buttons, fixed buttons] // menusActive [menuchannel, menuhandle, time, page] // // by SimonT Quinnell // // ********************************************************************
// ********************************************************************
// CONSTANTS
// ********************************************************************
// Link Commands integer LINK_MENU_DISPLAY = 300; integer LINK_MENU_CLOSE = 310; integer LINK_MENU_RETURN = 320; integer LINK_MENU_TIMEOUT = 330;
// Main Menu Details string BACK = "<<"; string FOWARD = ">>"; list MENU_NAVIGATE_BUTTONS = [ " ", "Back", "Exit"]; float MENU_TIMEOUT_CHECK = 10.0; integer MENU_TIMEOUT = 120; integer MAX_TEXT = 510;
integer STRIDE_DESCRIPTION = 8;
integer STRIDE_ACTIVE = 4;
integer DEBUG = FALSE;
// ******************************************************************** // Variables // ********************************************************************
list menusDescription; list menusActive;
// ********************************************************************
// Functions - General
// ********************************************************************
debug(string debug) {
if (DEBUG) llWhisper(0,"DEBUG:"+llGetScriptName()+":"+debug+" : Free("+(string)llGetFreeMemory()+")");
}
integer string2Bool (string test)
{
if (test == "TRUE") return TRUE; else return FALSE;
}
// ******************************************************************** // Functions - Menu Helpers // ********************************************************************
integer NewChannel() { // generates unique channel number
integer channel;
do channel = -(llRound(llFrand(999999)) + 99999); while (~llListFindList(menusDescription, [channel]));
return channel;
}
string CheckTitleLength(string title)
{
if (llStringLength(title) > MAX_TEXT) title = llGetSubString(title, 0, MAX_TEXT-1); return title;
}
list FillMenu(list buttons)
{ //adds empty buttons until the list length is multiple of 3, to max of 12
integer i; list listButtons; for(i=0;i<llGetListLength(buttons);i++) { string name = llList2String(buttons,i); if (llStringLength(name) > 24) name = llGetSubString(name, 0, 23); listButtons = listButtons + [name]; } while (llGetListLength(listButtons) != 3 && llGetListLength(listButtons) != 6 && llGetListLength(listButtons) != 9 && llGetListLength(listButtons) < 12) { listButtons = listButtons + [" "]; } buttons = llList2List(listButtons, 9, 11); buttons = buttons + llList2List(listButtons, 6, 8); buttons = buttons + llList2List(listButtons, 3, 5); buttons = buttons + llList2List(listButtons, 0, 2); return buttons;
}
RemoveUser(key id) {
integer index_id = llListFindList(menusDescription, [id]); while (~index_id) { integer channel = llList2Integer(menusDescription, index_id-1); RemoveMenu(channel, FALSE); }
}
RemoveMenu(integer channel, integer echo) {
integer index = llListFindList(menusDescription, [channel]);
if (index != -1) { key id = llList2Key(menusDescription, index+1); menusDescription = llDeleteSubList(menusDescription, index, index + STRIDE_DESCRIPTION -1); RemoveListen(channel);
if (echo) llMessageLinked(LINK_THIS, LINK_MENU_TIMEOUT, "Timeout", id); }
}
RemoveListen(integer channel) {
integer index = llListFindList(menusActive, [channel]); if (index != -1) { llListenRemove(llList2Integer(menusActive, index + 1)); menusActive = llDeleteSubList(menusActive, index, index + STRIDE_ACTIVE - 1); }
}
// ******************************************************************** // Functions - Menu Main // ********************************************************************
NewMenu(string message, integer senderNum, key id) { // Setup New Menu
// menusDescription [menuchannel, key, menu & parent, return link, nav?, titles, buttons, fixed buttons]
list temp = llParseString2List(message, ["|"], []); integer channel = NewChannel(); if (llGetListLength(temp) > 2) { menusDescription = [channel, id, llList2String(temp, 0), senderNum, string2Bool(llList2String(temp, 1)), llList2String(temp, 2), llList2String(temp, 3), llList2String(temp, 4)] + menusDescription;
DisplayMenu(id, channel, 0); } else llOwnerSay ("ERROR: Dialog Script. Incorrect menu format");
}
DisplayMenu(key id, integer channel, integer page) {
string menuTitle; list menuSubTitles; list menuButtonsAll; list menuButtons; list menuNavigateButtons; list menuFixedButtons;
integer max = 12;
// Populate values integer index = llListFindList(menusDescription, [channel]); menuButtonsAll = llParseString2List(llList2String(menusDescription, index+6), ["~"], []); if (llList2String(menusDescription, index+7) != "") menuFixedButtons = llParseString2List(llList2String(menusDescription, index+7), ["~"], []);
// Set up the menu buttons if (llList2Integer(menusDescription, index+4)) menuNavigateButtons= MENU_NAVIGATE_BUTTONS; else if (llGetListLength(menuButtonsAll) > (max-llGetListLength(menuFixedButtons))) menuNavigateButtons = [" ", " ", " "]; // FIXME: add sanity check for menu page max = max - llGetListLength(menuFixedButtons) - llGetListLength(menuNavigateButtons); integer start = page*max; integer stop = (page+1)*max - 1; menuButtons = FillMenu(menuFixedButtons + llList2List(menuButtonsAll, start, stop));
// Generate the title list tempTitle = llParseString2List(llList2String(menusDescription, index+5), ["~"], []); menuTitle = llList2String(tempTitle,0); if (llGetListLength(tempTitle) > 1) menuSubTitles = llList2List(tempTitle, 1, -1); if (llGetListLength(menuSubTitles) > 0) { integer i; for(i=start;i<(stop+1);++i) { if (llList2String(menuSubTitles, i) != "") menuTitle += "\n"+llList2String(menuSubTitles, i); } } menuTitle = CheckTitleLength(menuTitle); // Add navigate buttons if necessary if (page > 0) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [BACK], 0, 0); if (llGetListLength(menuButtonsAll) > (page+1)*max) menuNavigateButtons = llListReplaceList(menuNavigateButtons, [FOWARD], 2, 2); // Set up listen and add the row details integer menuHandle = llListen(channel, "", id, ""); menusActive = [channel, menuHandle, llGetUnixTime(), page] + menusActive;
llSetTimerEvent(MENU_TIMEOUT_CHECK);
// Display menu llDialog(id, menuTitle, menuNavigateButtons + menuButtons, channel);
}
// ********************************************************************
// Event Handlers
// ********************************************************************
default {
listen(integer channel, string name, key id, string message) { if (message == BACK) { integer index = llListFindList(menusActive, [channel]); integer page = llList2Integer(menusActive, index+3)-1; RemoveListen(channel); DisplayMenu(id, channel, page); } else if (message == FOWARD) { integer index = llListFindList(menusActive, [channel]); integer page = llList2Integer(menusActive, index+3)+1; RemoveListen(channel); DisplayMenu(id, channel, page); } else if (message == " ") { integer index = llListFindList(menusActive, [channel]); integer page = llList2Integer(menusActive, index+3); RemoveListen(channel); DisplayMenu(id, channel, page); } else { integer index = llListFindList(menusDescription, [channel]); llMessageLinked(llList2Integer(menusDescription, index+3), LINK_MENU_RETURN, llList2String(menusDescription, index+2)+"|"+message, id); RemoveMenu(channel, FALSE); } } link_message(integer senderNum, integer num, string message, key id) { if (num == LINK_MENU_DISPLAY) NewMenu(message, senderNum, id); else if (num == LINK_MENU_CLOSE) RemoveUser(id); } timer() { // Check through timers and close if necessary integer i; list toRemove; integer currentTime = llGetUnixTime(); integer length = llGetListLength(menusActive); for(i=0;i<length;i+=STRIDE_ACTIVE) { if (currentTime - llList2Integer(menusActive, i+2) > MENU_TIMEOUT) toRemove = [llList2Integer(menusActive, i)] + toRemove; }
length = llGetListLength(toRemove); if (length > 0) { for(i=0;i<length;i++) { RemoveMenu(llList2Integer(toRemove, i), TRUE); } } }
}
</lsl>