Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

Reading a string with linebreaks from a notecard to a hud script?

Dei Zhao
Registered User
Join date: 10 Oct 2006
Posts: 6
03-10-2007 03:36
Hi everyone, I'm hoping someone knows a solution to this...

I'm trying to read a multiline string from a note card to a parsed list in a HUD script. The parsed list will then be read into various string variables that will display notes onto the hud. The user pressed a button on the HUD menu and the note is displayed. My problem is that the parsing is cutting up my string at the line breaks. Is there a way to read multiple lines into a parsed list like this? Ive been searching for hours on the WIKI and in here in the forums to no avail :(. Im sure this has been answered before but i cant find it for the life of me...if anyone can post some example code or point me in the right direction it would be much appreciated. Thanks :)

Example Notecard Info:

** trying to parse from ~ to ~


~Title 1: blah blah blah (line break)
Heading 1 -- this and that (line break) (this would be the 1st string in the list)
Heading 2 -- text goes here (line break)

~Title 2: blah blah blah (line break)
Heading 1 -- this and that (line break) (second string)
Heading 2 -- text goes here (line break

and so on...




Example Code:


default
{


on_rez(integer param)
{
llResetScript();
}



state_entry()
{

//llSetText("Initializing...", <1.0, 0.0, 0.0>, 1.0);

if (llGetInventoryType(sSettingsNotecard) == INVENTORY_NOTECARD)
kCurrentDataRequest = llGetNotecardLine( sSettingsNotecard, iNoteCardLine );
else
llWhisper( 0, "Using Default Values." );
CancelListen();
}


dataserver( key kQuery, string sData )
{
//CDetailsList = llParseString2List(sData, ["~"], []); ***This line isnt working

kCurrentDataRequest = "";


}


touch_start(integer total_number)
{
key id = llDetectedKey(0);
UpdateListen(id);
llDialog(id, "Model Main MENU", MENU_MAIN, DialogChannel ); // present dialog on click
}


listen( integer channel, string name, key id, string message)
{

integer index = llListFindList(MENU_MAIN, [message]);

if(index >= 0)
{
if(0 == index)
{
llDialog(id, "TEXT", MENU_1, DialogChannel );
MyString= llList2String(CDetailsList, 0); //trying to read from the parsed list to this
MyString2= llList2String(CDetailsList, 1);

}
...


I tried this too ----> llDumpList2String(llParseStringKeepNulls(sData, ["\\n"],[]),"\n";);..but it didnt work the way i need it to...or at least i couldnt figure out how to make it work this way
Newgate Ludd
Out of Chesse Error
Join date: 8 Apr 2005
Posts: 2,103
03-10-2007 03:50
When you say line break do you mean that the line is on 2 physically different lines in the notecard? If so then no llGetNotecardLine will read single line, a line break is a line terminator.

Your best bet is to use a terminator marker within the text and then concatenate lines read until the terminator is read.

Can you post an actual notecard so that we can make a bit more sense of what you are trying to do?
Dei Zhao
Registered User
Join date: 10 Oct 2006
Posts: 6
03-10-2007 04:30
Hi Newgate..thanks for responding. Yes the lines are physically on different lines in the note card...here is a piece of the notecard im trying to read



~Model 1:

*Item 1 --
*Item 2 --
*Item 3 --
*Item 4 --
*Item 5 --
*Other --

~Model 2:

*Item 1 --
*Item 2 --
*Item 3 --
*Item 4 --
*Item 5 --
*Other --

~Model 3:

*Item 1 --
*Item 2 --
*Item 3 --
*Item 4 --
*Item 5 --
*Other --


so im tryin to get it like this

string Model1 = "Model 1:

*Item 1 --
*Item 2 --
*Item 3 --
*Item 4 --
*Item 5 --
*Other --";

string Model2 = "Model 2:

*Item 1 --
*Item 2 --
*Item 3 --
*Item 4 --
*Item 5 --
*Other --";

etc... And I'm trying to keep the notecard seperated this way so that the user can just fill in the information with it being overly complicated and hard to understand.

Hope that clears it up a bit. Thanks for ur help :)
Newgate Ludd
Out of Chesse Error
Join date: 8 Apr 2005
Posts: 2,103
03-10-2007 05:22
I'm still not 100% sure what you dont seem to be able to get working. Nothing in your example notecard appears to require 2 lines?

Given that each new Model will starts with ~Model use that as a delimiter to recognise the start of a new entry as I mentioned in my first reply.
Newgate Ludd
Out of Chesse Error
Join date: 8 Apr 2005
Posts: 2,103
03-10-2007 05:40
The following should read a notecard of the form you described adding each Model section, including its items, as seperate 'lines' within a single string which is then added to a list.

CODE

// Menu problem for Dei Zhao
string sSettingsNotecard = "Settings"; // Notecard containing data
integer lineCounter; // Line number within the notecard
key dataRequestID; // Data request ID
list menuItems;
string strLine;

default
{
on_rez(integer num) { llResetScript(); }

state_entry()
{
llOwnerSay("Initialising. Please Wait.");
menuItems = [];
lineCounter = 0;
strLine = "";

integer itemtype = llGetInventoryType(sSettingsNotecard);
if(INVENTORY_NOTECARD == itemtype)
{
dataRequestID = llGetNotecardLine(sSettingsNotecard, lineCounter);
llSetTimerEvent(10);
}
else
{
llOwnerSay("Error - Settings notecard missing!");
state Running;
}

}
dataserver(key queryid, string data)
{
//Check to make sure this is the request we are making.
//Remember that when data comes back from the dataserver,
//it goes to *all* scripts in your prim.
//So you have to make sure this is the data you want, and
//not data coming from some other script.
if (dataRequestID)
{
llSetTimerEvent(0);
//If we haven't reached the end of the file
if (data != EOF)
{
if(";" != llGetSubString(data, 0,0))
{
if("~Model" == llGetSubString(data,0,5) )
{
if(llStringLength(strLine) > 0)
{
menuItems = (menuItems = []) + menuItems + [ strLine ];
strLine = "";
}
}
strLine += "\n" + data;
}
++lineCounter;
dataRequestID = llGetNotecardLine(sSettingsNotecard, lineCounter);
llSetTimerEvent(10);

}
else
{
llSetTimerEvent(0);
if(llStringLength(strLine) > 0)
{
menuItems = (menuItems = []) + menuItems + [ strLine ];
strLine = "";
}
state Running;
}
}
}

timer()
{
// The notecard read failed so abort
llSetTimerEvent(0);
llOwnerSay("Error reading Data.Aborting.");
state Running;
}

changed(integer change)
{
if(change & CHANGED_INVENTORY)llResetScript();
}
}
// -----------------------------------------------------------
state Running
{
state_entry()
{
llOwnerSay("Ready.");
}
}


Your touch handler etc should be moved in to the running state, that way it will only be touchable when its read the notecard and is ready.

How you manipulate the data after this is upto you.


Another approach would be to use strided lists, which in this instance I think would be simpler but slightly less memory efficient.
Pale Spectre
Registered User
Join date: 2 Sep 2005
Posts: 586
03-10-2007 06:03
'\n' will give you line breaks inside a string but getting them in there properly can be tricky.

For example: llDumpList2String(llParseStringKeepNulls(sData, ["\\n"],[]),"\n";);

...assumes you start with: "Line1\\nLines2" and want "Line1\nLine2"

Which might be right. :) but it depends on what was read from the notecard.

"Line1\nLine2" would be read from the notecard as: "Line1\\nLine2"

...confusing, but the '\' is an escape character and will itself get 'escaped' when read.

From what I understand of your examples I think you're after a dataserver Event Handler something like this:


CODE
dataserver(key queryid, string data)
{
if(queryid == dataserver_key)
{
if(data != EOF)
{
if(llGetSubString(data, 0, 0) == "~") // next Model
{
if (tmpString != "") CDetailsList += [tmpString];
tmpString = llGetSubString(data, 1, -1);
}
else tmpString += "\n" + data;

iNoteCardLine++;
dataserver_key = llGetNotecardLine(sSettingsNotecard, iNoteCardLine);

}
else CDetailsList += [tmpString];
}
}
Note: tmpString needs to be a Global variable

[I know Mr Newgate beat me too it but I did a somewhat more basic example of just the datasever Event Handler. I also wonder if cleverness like: menuItems = (menuItems = []) + menuItems + [ strLine ]; is going to confuse people. Apparently a more memory efficient way of appending Lists but not very intuitive. :p

My example also assumed that Model n can be any description and starts the string with that description not including the tilde. I'm using the tildes to determine the beginning of each string.]
Dei Zhao
Registered User
Join date: 10 Oct 2006
Posts: 6
03-10-2007 06:31
That looks great! I just tried it but now my touch to start function isnt working...because of the running state? So, I cant tell if it worked. K heres what it did...i added your code at the begining of my default...above the touch start and the rest of my hud menu code....then put the state_running function after default...does that make sense? It reads the notecard and displays the ready msg....but i need to be abl to touch the object and have the menu pop up. The hud is going to be in a prim that will be rez'd in world..with multiple ppl accessing its menu at about the same time (kinda like a sexgen bed menu...without the fun stuff). Sorry if im being annoyingly unclear...this is my first major LSL attempt besides making minor changes in pre-existing scripts so im a total noob :P...thanks again for your help.



changed(integer change)
{
if(change & CHANGED_INVENTORY)llResetScript();
}



// ----------------------touch start-------------------------------------



touch_start(integer total_number)
{
key id = llDetectedKey(0);
UpdateListen(id);
llDialog(id, "Model Main MENU", MENU_MAIN, DialogChannel ); // present dialog on click
}
Newgate Ludd
Out of Chesse Error
Join date: 8 Apr 2005
Posts: 2,103
03-10-2007 13:53
As I said in my previous posting MOVE the touch handler into the running state.
The way I have recoded it, the default state is only used to initialise the system by reading the notecard. As soon as the notecard is read the script switches state to the Running state so all user interaction should now take place there.

If this is going to be a HUD then only 1 person will ever touch it, the wearer.

As for Pale's concern about my confusing code, he does have a point, but since we did not have knowledge of the number of items we may be reasding from the notecard I decided to err on the side of efficiency.
Dei Zhao
Registered User
Join date: 10 Oct 2006
Posts: 6
03-11-2007 00:45
Thanks so much Newgate and Pale. I finally got it working using a combo of the two versions y'all posted. Now if I can find an easy way to read in multiple notecards Ill be set :) Thanks again for your time.
Pale Spectre
Registered User
Join date: 2 Sep 2005
Posts: 586
03-11-2007 03:34
The secret to multiple notecards - or, in fact, dataserver request for more than one notecard - is in the dataserver queryid.

So, imagine you have requests for:

dataserver_key1 = llGetNotecardLine(sSettingsNotecard, iNoteCardLine);

in conjunction with: if(queryid == dataserver_key1)

When EOF occurs for the first notecard you simply switch to:

dataserver_key2 = llGetNotecardLine(sSettingsNotecard, iNoteCardLine);

in conjucntion with: if(queryid == dataserver_key2)

Of course, you also have to switch the value for sSettingsNotecard and reset iNoteCardLine but I hope you can see how in this way the dataserver Handler Event carry logic for many different 'shapes' of notecard.
Dei Zhao
Registered User
Join date: 10 Oct 2006
Posts: 6
03-13-2007 15:07
Got it! It all works perfectly. Thanks for all your help! :)