Don't click here unless you want to be banned.

LSL Wiki : LibraryCBMenuLineButtonScrolling

HomePage :: PageIndex :: RecentChanges :: RecentlyCommented :: UserSettings :: You are ia360925.us.archive.org
// MenuLineButtonScrolling
// Responsible for ensuring that scrolling the screen still
// keeps corresponding buttons and corresponding lines together.
// stores enableMenuLineButton and disableMenuLineButton data 
// and enables/disables  line buttons
// in responce to scrollUp and scrollDown events

// Copyright (C) 2005-2006  Francisco V. Saldana
// 
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
// 
// Francisco V. Saldana can be contacted using his email account 
//  username: dressedinblue, at domain: gmail.com
// and in Second Life by IMming Christopher Omega

// Number of lines to scroll down for each press of a scroll button.
integer LINES_PER_SCROLL = 12;

// Library functions: 

// replaceListSlice()
// Replaces the list elements in dest with elements in src, starting at start.
// For example, if dest was [A, B, C, D], src was [E, F] and start was 1, 
// the list returned would be [A, E, F, D]. 
// If llGetListLength(src) + start > llGetListLength(dest), the returned list length
// will be greater then dest list's length.

string NULL = "";
list replaceListSlice(list dest, list src, integer start) {
    if (llGetListEntryType(dest, start - 1) == TYPE_INVALID) {
        integer len;
        for(len = llGetListLength(dest); len < start; len++) {
            dest += NULL;
        }
    }
    integer srcLen = llGetListLength(src);
    return llListInsertList(llDeleteSubList(dest, start, start + srcLen - 1), src, start);
}

// ========== For method invocation ==========
string randomStr(string chars, integer len) {
    integer numChars = llStringLength(chars);
    string ret;
    integer i;
    for (i = 0; i < len; i++) {
        integer randIndex = llFloor(llFrand(numChars));
        ret += llGetSubString(chars, randIndex, randIndex);
    }
    return ret;
}

string SEPERATOR_CHARS = "`~!@#$%^&*()-_+[]{}\|'\";/?.>,<";
integer SEPERATOR_LEN  = 3;
string dumpList2String(list src) {
    // Generate a seperator not present in any of the
    // elements in the list.
    string chars = (string) src; // Squashes all elements together.
    string seperator;
    do {
        seperator = randomStr(SEPERATOR_CHARS, SEPERATOR_LEN);
    } while (llSubStringIndex(chars, seperator) != -1);
    return seperator + llDumpList2String(src, seperator);
}

list parseStringKeepNulls(string src) {
    // The seperator should be the first SEPERATOR_LEN
    // characters in the string.
    return llParseStringKeepNulls(llDeleteSubString(src, 0, SEPERATOR_LEN - 1),
        [llGetSubString(src, 0, SEPERATOR_LEN - 1)], []);
}

callMethod(integer callId, string methodName, list parameters) { 
    llMessageLinked(LINK_THIS, callId,
        dumpList2String(parameters), methodName);
}

returnValue(string methodName, integer methodIdentifyer, list value) {
    llMessageLinked(LINK_THIS, methodIdentifyer, 
        dumpList2String(value), methodName + "_ret");
}
// =============================================

trigger_menuLineButtonPressed(integer line, list detectedData) {
    callMethod(0, "menuLineButtonPressed", [line, dumpList2String(detectedData)]);
}

trigger_enableLineButton(integer line) {
    callMethod(0, "enableLineButton", [line]);
}

trigger_disableLineButton(integer line) {
    callMethod(0, "disableLineButton", [line]);
}

trigger_enableAllLineButtons() {
    callMethod(0, "enableAllLineButtons", []);
}

trigger_disableAllLineButtons() {
    callMethod(0, "disableAllLineButtons", []);
}

integer getMenuTextAreaPositionRetVal;
trigger_getMenuTextAreaPosition() {
    getMenuTextAreaPositionRetVal = (integer) llFrand(13831);
    callMethod(getMenuTextAreaPositionRetVal, "getMenuTextAreaPosition", []);
}

trigger_pong(string moduleName) {
    callMethod(0, "pong", [moduleName]);
}

// Global variables manipulated by method calls (link messages):

// Stores data about the status of the buttons corresponding to lines.
// since each line has a button associated with it, the scroll module 
// needs to store if the buttons are enabled or not.
// It does this as efficiantly as possible, by storing buttons statuses
// as bitfields in the textButtons list; the first index (0) in textButtons
// stores the bitfield containing info for the first 32 buttons.
list textButtons = [];
integer buttonCount;

integer isLineButtonEnabled(integer line) {
    // Since there are 32 line-button statuses stored in each index, 
    // divide the line number by 32, and round the result. 
    integer bitfieldIndex = line / 32;
    // Get the bitfield at the index.
    integer bitfield      = llList2Integer(textButtons, bitfieldIndex);
    // Get the position of the line-button bit in the bitfield. 
    integer bitPosition   = line - (bitfieldIndex * 32);
    // Get the value of the bit. 
    integer bit           = llFloor(llPow(2, bitPosition));
    return (bitfield & bit) == bit;
}

disableLineButton(integer line) {
    // See isLineButtonEnabled for more comments about this process.
    integer bitfieldIndex = line / 32;
    integer bitfield      = llList2Integer(textButtons, bitfieldIndex);
    integer bitPosition   = line - (bitfieldIndex * 32);
    integer bit           = llFloor(llPow(2, bitPosition));
    
    // AND the masked-out bit to the bitfield.
    bitfield = bitfield & ~bit;
    // Store the updated bitfield back into the list.
    textButtons = replaceListSlice(textButtons, [bitfield], bitfieldIndex);
}

enableLineButton(integer line) {
    // See isLineButtonEnabled for more comments about this process.
    integer bitfieldIndex = line / 32;
    integer bitfield      = llList2Integer(textButtons, bitfieldIndex);
    integer bitPosition   = line - (bitfieldIndex * 32);
    integer bit           = llFloor(llPow(2, bitPosition));
    
    // OR the bit in the bitfield.
    bitfield = bitfield | bit;
    // Store the updated bitfield back into the list.
    textButtons = replaceListSlice(textButtons, [bitfield], bitfieldIndex);
}

// The index in textStrings that is displayed on the topmost line of the screen.
integer indexAtTop = 0;

integer firstMenuLine;
integer lastMenuLine;
integer menuLineTotal;

string buttonWriter;
string screenWriter;
// A boolean telling if the scroller should ignore writes to the screen
// or not. Since the scroller calls the same method other components would 
// to write to the screen, it should ignore itself.
//integer ignoreScreenWrites = FALSE;

string this;
default {
    state_entry() {
        this = llGetScriptName();
        trigger_getMenuTextAreaPosition();
    }
    link_message(integer sender, integer num, string parameters, key methodName) {
        if (methodName == "getMenuTextAreaPosition_ret" && num == getMenuTextAreaPositionRetVal) {
            list paramList = parseStringKeepNulls(parameters);
            // Return value format:
            // getMenuTextAreaPosition_ret(integer startline, integer endLine)
            integer startLine = (integer) llList2String(paramList, 0);
            integer endLine   = (integer) llList2String(paramList, 1);
            firstMenuLine = startLine;
            lastMenuLine  = endLine;
            menuLineTotal = (lastMenuLine - firstMenuLine) + 1;
            state main;
        }
    }
}

state main {
    link_message(integer sender, integer num, string parameters, key methodName) {
        if (methodName == "menuScrollUp") {
            indexAtTop -= LINES_PER_SCROLL;
            
            // If we've reached the first index of the text data:
            if (indexAtTop <= 0) {
                indexAtTop = 0;
            }
            
            // Notify the other modules of a menu screen-position change:
            //trigger_setStartLineOffset(indexAtTop);
            // Set the screen to the scrolled values:
            integer screenLine;
            for (screenLine = firstMenuLine; screenLine <= lastMenuLine; screenLine++) {
                integer textIndex = screenLine + indexAtTop;
                if (isLineButtonEnabled(textIndex)) {
                    trigger_enableLineButton(screenLine);
                } else {
                    trigger_disableLineButton(screenLine);
                }
            }
        } else if (methodName == "menuScrollDown") {
            indexAtTop += LINES_PER_SCROLL;
            
            // Make sure indexAtTop is valid:
            // If there's not enough text data to fill the screen when we've scrolled down:
            if (buttonCount - indexAtTop < menuLineTotal) {
                // Adjust so that the last text data element is on the last line
                // of the screen.
                indexAtTop = buttonCount - menuLineTotal;
            }
            
            //trigger_setStartLineOffset(indexAtTop);
            integer screenLine;
            for (screenLine = firstMenuLine; screenLine <= lastMenuLine; screenLine++) {
                integer textIndex = screenLine + indexAtTop;
                if (isLineButtonEnabled(textIndex)) {
                    trigger_enableLineButton(screenLine);
                } else {
                    trigger_disableLineButton(screenLine);
                }
            }
        } else if (methodName == "lineButtonPressed") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // lineButtonPressed(integer lineNumber, list detectedData)
            integer lineNumber   = (integer) llList2String(paramList, 0);
            list    detectedData = parseStringKeepNulls(llList2String(paramList, 1));
            trigger_menuLineButtonPressed(lineNumber + indexAtTop, detectedData);
        } else if (methodName == "enableMenuLineButton") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // enableMenuLineButton(string moduleName, integer lineNumber)
            string  moduleName =           llList2String(paramList, 0);
            integer lineNumber = (integer) llList2String(paramList, 1);
            if (moduleName != buttonWriter)
                return;
            enableLineButton(lineNumber);
            
            if (lineNumber >= firstMenuLine + indexAtTop && lineNumber <= lastMenuLine + indexAtTop) {
                trigger_enableLineButton(lineNumber - indexAtTop);
            }
        } else if (methodName == "enableAllMenuLineButtons") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // enableAllMenuLineButtons(string moduleName)
            // Note: Was ignorable.
            string moduleName = llList2String(paramList, 0);
            if (moduleName != buttonWriter)
                return;
                
            integer i;
            integer numBitfields = llGetListLength(textButtons);
            for (i = 0; i < numBitfields; i++) {
                // Replace the bitfield at the index with -1, this will effectively
                // make all bits 1. 
                textButtons = replaceListSlice(textButtons, [-1], i);
            }
            trigger_enableAllLineButtons();
        } else if (methodName == "disableMenuLineButton") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // disableLineButton(string moduleName, integer lineNumber)
            string  moduleName =           llList2String(paramList, 0);
            integer lineNumber = (integer) llList2String(paramList, 1);
            if (moduleName != buttonWriter)
                return;
                
            // Note: Was ignorable.
            disableLineButton(lineNumber);
            
            if (lineNumber >= firstMenuLine + indexAtTop && lineNumber <= lastMenuLine + indexAtTop) {
                trigger_disableLineButton(lineNumber - indexAtTop);
            }
        } else if (methodName == "disableAllMenuLineButtons") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // disableAllMenuLineButtons(string moduleName)
            // Note: Was ignorable.
            // should I set indexAtTop to 0?
            string moduleName = llList2String(paramList, 0);
            if (moduleName != buttonWriter)
                return;
            indexAtTop = 0;
            buttonCount = 0;
            textButtons = [];
            trigger_disableAllLineButtons();
        } else if (methodName == "setMenuLineText") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // setMenuLineText(string moduleName, integer line, string text)
            string  moduleName =           llList2String(paramList, 0);
            integer line       = (integer) llList2String(paramList, 1);
            string  text       =           llList2String(paramList, 2);
            if (moduleName != screenWriter)
                return;
            if (buttonCount - 1 < line)
                buttonCount = line + 1;
        } else if (methodName == "setButtonWriter") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // setScreenWriter(string moduleName)
            string moduleName = llList2String(paramList, 0);
            buttonWriter = moduleName;
        } else if (methodName == "setScreenWriter") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // setScreenWriter(string moduleName)
            string moduleName = llList2String(paramList, 0);
            screenWriter = moduleName;
        } else if (methodName == "ping") {
            list paramList = parseStringKeepNulls(parameters);
            // Method signature:
            // ping(string moduleName)
            string moduleName = llList2String(paramList, 0);
            if (moduleName == this)
                trigger_pong(this);
        }
    }
}


LibraryContentBrowser
There is no comment on this page. [Display comments/form]