still finding fudge-up on startup
[feisty_meow.git] / huffware / huffotronic_scripts / FreeView_v1.8.txt
1 
2 // huffware script: freeview modified by fred huffhines (see original license below).
3 //
4 // my changes are licensed this way:
5 //   this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html
6 //   do not use it in objects without fully realizing you are implicitly accepting that license.
7 //
8 // fred's changes include:
9 // + assorted tweaks that i have since forgotten the details about.
10 // + set the freeview to startup as a picture viewer, since that's my most common usage.
11
12 //////////////
13 // original author info and licensing:
14 //FreeView 1.2 WebGuide (revision 4) - By CrystalShard Foo
15 //Multifunctional Picture viewer and Video control script with webguide support
16 //This script is distributed for free and must stay that way. 
17 //      *** If you wish to give/sell a product using this script,  ***
18 //      ***     THEN THE SCRIPT MUST REMAIN FULL-PERM AND FREE.    ***
19 //      ***   Failure to do so will REVOKE your right to use it!   ***
20 //Help for using this script can be obtained at: http://www.slguide.com/help
21 //Feel free to modify this script and post your improvement. Leave the credits intact but feel free to add your name at its bottom.
22 //Whats new:
23 //- Now using FULL_BRIGHT instead of PRIM_MATERIAL_LIGHT for the screen display
24 //- Added an ownership-change code to handle cases where FreeView gets deeded to group post Video Init.
25 //- Renamed WebGuide to TV-Guide to reflect what this thing does better.
26 //- Added a 'Fix Scale' button to Picture mode to help against user texture-scale changes.
27 //- Additional minor help-tips and code improvements
28 //Enjoy!
29 //////////////
30
31
32 //Constants
33 integer PICTURE_ROTATION_TIMER = 30;   //In whole seconds
34
35 integer DISPLAY_ON_SIDE = ALL_SIDES; //Change this to change where the image will be displayed
36
37 key VIDEO_DEFAULT = "71b8ff26-087d-5f44-285b-d38df2e11a81";  //Test pattern - Used as default video texture when one is missing in parcel media
38 key BLANK = "5748decc-f629-461c-9a36-a35a221fe21f"; //Blank texture - Used when there are no textures to display in Picture mode
39 string NOTECARD = "bookmarks";  //Used to host URL bookmarks for video streams
40
41 integer VIDEO_BRIGHT = TRUE;    //FULL_BRIGHT status for Video
42 integer PICTURE_BRIGHT = TRUE;  //FULL_BRIGHT status for Picture
43
44 integer REMOTE_CHANNEL = 9238742;
45
46 integer EXTERNAL_TOUCH_CHANNEL = 1327;
47     // used by other prims to tell the viewer prim that the avatar has clicked on them.
48
49 integer mode = 0;           //Freeview mode.
50                             //Mode 0 - Power off
51                             //Mode 1 - Picture viewer
52                             //Mode 2 - Video
53
54 integer listenHandle = -1;      //Dialog menu listen handler
55 integer listenUrl = -1;         //listen handler for channel 1 for when a URL is being added
56 integer listenTimer = -1;       //Timer variable for removing all listeners after 2 minutes of listener inactivity
57 integer listenRemote = -1;      //listen handler for the remote during initial setup
58 integer encryption = 0;
59 integer numberofnotecardlines = 0;  //Stores the current number of detected notecard lines.
60 integer notecardline = 0;       //Current notecard line
61
62 integer loop_image = FALSE;     //Are we looping pictures with a timer? (picture mode)
63 integer current_texture = 0;    //Current texture number in inventory being displayed (picture mode)
64 integer chan;                   //llDialog listen channel
65 integer notecardcheck = 0;
66 key video_texture;              //Currently used video display texture for parcel media stream
67
68 string moviename;
69 string tempmoviename;
70 key notecardkey = NULL_KEY;
71 key tempuser;                   //Temp key storge variable
72 string tempurl;                 //Temp string storge variable
73
74 integer isGroup = TRUE;
75 key groupcheck = NULL_KEY;
76 key last_owner;
77 key XML_channel;
78
79 pictures()      //Change mode to Picture Viewer
80 {
81     //Initilize variables
82     
83     //Change prim to Light material while coloring face 0 black to prevent light-lag generation.
84     llSetPrimitiveParams([PRIM_BUMP_SHINY, DISPLAY_ON_SIDE, PRIM_SHINY_NONE, PRIM_BUMP_NONE, PRIM_COLOR, DISPLAY_ON_SIDE, <1,1,1>, 1.0, PRIM_MATERIAL, PRIM_MATERIAL_PLASTIC, PRIM_FULLBRIGHT, DISPLAY_ON_SIDE, PICTURE_BRIGHT]);
85
86     integer check = llGetInventoryNumber(INVENTORY_TEXTURE);
87      
88     if(check == 0)
89     {
90         report("No pictures found.");
91         llSetTexture(BLANK,DISPLAY_ON_SIDE);
92         return;
93     }
94     else    
95         if(current_texture > check)
96             //Set to first texture if available
97             current_texture = 0;
98             
99     display_texture(current_texture);
100 }
101
102 video()         //Change mode to Video
103 {
104     //Change prim to Light material while coloring face 0 black to prevent light-lag generation.
105     llSetPrimitiveParams([PRIM_BUMP_SHINY, DISPLAY_ON_SIDE, PRIM_SHINY_NONE, PRIM_BUMP_NONE, PRIM_COLOR, DISPLAY_ON_SIDE, <1,1,1>, 1.0, PRIM_MATERIAL, PRIM_MATERIAL_PLASTIC, PRIM_FULLBRIGHT, DISPLAY_ON_SIDE, VIDEO_BRIGHT, PRIM_TEXTURE, DISPLAY_ON_SIDE, "62dc73ca-265f-7ca0-0453-e2a6aa60bb6f", llGetTextureScale(DISPLAY_ON_SIDE), llGetTextureOffset(DISPLAY_ON_SIDE), llGetTextureRot(DISPLAY_ON_SIDE)]);
106     
107     report("Video mode"+moviename+": Stopped");
108     if(finditem(NOTECARD) != -1)
109         tempuser = llGetNumberOfNotecardLines(NOTECARD);
110     video_texture = llList2Key(llParcelMediaQuery([PARCEL_MEDIA_COMMAND_TEXTURE]),0);
111     if(video_texture == NULL_KEY)
112     {
113         video_texture = VIDEO_DEFAULT;
114         llParcelMediaCommandList([PARCEL_MEDIA_COMMAND_TEXTURE,VIDEO_DEFAULT]);
115         llSay(0,"No parcel media texture found. Setting texture to default: "+(string)VIDEO_DEFAULT);
116         if(llGetLandOwnerAt(llGetPos()) != llGetOwner())
117             llSay(0,"Error: Cannot modify parcel media settings. "+llGetObjectName()+" is not owned by parcel owner.");
118     }
119     
120     llSetTexture(video_texture,DISPLAY_ON_SIDE);
121 }
122
123 off()
124 {
125     report("Click to power on.");
126     llSetPrimitiveParams([PRIM_BUMP_SHINY, DISPLAY_ON_SIDE, PRIM_SHINY_LOW, PRIM_BUMP_NONE, PRIM_COLOR, DISPLAY_ON_SIDE, <0.1,0.1,0.1>, 1.0,PRIM_MATERIAL, PRIM_MATERIAL_PLASTIC, PRIM_FULLBRIGHT, DISPLAY_ON_SIDE, FALSE, PRIM_TEXTURE, DISPLAY_ON_SIDE, BLANK, llGetTextureScale(DISPLAY_ON_SIDE), llGetTextureOffset(DISPLAY_ON_SIDE), llGetTextureRot(DISPLAY_ON_SIDE)]);
127 }
128
129 integer finditem(string name)   //Finds and returns an item's inventory number
130 {
131     integer i;
132     for(i=0;i<llGetInventoryNumber(INVENTORY_NOTECARD);i++)
133         if(llGetInventoryName(INVENTORY_NOTECARD,i) == NOTECARD)
134             return i;
135     return -1;
136 }
137
138 seturl(string url, key id)  //Set parcel media URL
139 {
140     if(mode != 2)
141     {
142         video();
143         mode = 2;
144     }
145     moviename = tempmoviename;
146     if(moviename)
147         moviename = " ["+moviename+"]";
148     tempmoviename = "";
149     string oldurl = llList2String(llParcelMediaQuery([PARCEL_MEDIA_COMMAND_URL]),0);
150     if(oldurl != "")
151         llOwnerSay("Setting new media URL. The old URL was: "+oldurl);
152
153     llParcelMediaCommandList([PARCEL_MEDIA_COMMAND_URL,url]);
154     if(id!=NULL_KEY)
155         menu(id);
156     else
157     {
158         report("Video mode"+moviename+": Playing");
159         llParcelMediaCommandList([PARCEL_MEDIA_COMMAND_PLAY]);
160     }
161        
162     if(isGroup)
163         llSay(0,"New media URL set.");
164     else
165         llOwnerSay("New media URL set: "+url);
166 }
167
168 string mediatype(string ext)    //Returns a string stating the filetype of a file based on file extension
169 {
170     ext = llToLower(ext);
171     if(ext == "swf")
172         return "Flash";
173     if(ext == "mov" || ext == "avi" || ext == "mpg" || ext == "mpeg" || ext == "smil")
174         return "Video";
175     if(ext == "jpg" || ext == "mpeg" || ext == "gif" || ext == "png" || ext == "pict" || ext == "tga" || ext == "tiff" || ext == "sgi" || ext == "bmp")
176         return "Image";
177     if(ext == "txt")
178         return "Text";
179     if(ext == "mp3" || ext == "wav")
180         return "Audio";
181     return "Unknown";
182 }
183
184 browse(key id)      //Image browser function for picture viewer mode
185 {
186     integer check = llGetInventoryNumber(INVENTORY_TEXTURE);
187     string header;
188     if(check > 0)
189         header = "("+(string)(current_texture+1)+"/"+(string)check+") "+llGetInventoryName(INVENTORY_TEXTURE,current_texture);
190     else
191         header = "No pictures found.";
192     llDialog(id,"** Monitor Control **\n Picture Viewer mode\n- Image browser\n- "+header,["Back","Next","Menu"],chan);
193     extendtimer();
194 }
195
196 report(string str)
197 {
198     llSetObjectDesc(str);
199 }
200
201 extendtimer()       //Add another 2 minute to the Listen Removal timer (use when a Listen event is triggered)
202 {
203     if(listenHandle == -1)
204         listenHandle = llListen(chan,"","","");
205     listenTimer = (integer)llGetTime() + 120;
206     if(loop_image == FALSE)
207         llSetTimerEvent(45);
208 }
209
210 config(key id)      //Configuration menu
211 {
212     extendtimer();
213     llDialog(id,"Current media URL:\n"+llList2String(llParcelMediaQuery([PARCEL_MEDIA_COMMAND_URL]),0)+"\nTip: If the picture is abit off, try 'Align ON'",["Set URL","Align ON","Align OFF","Menu","Set Remote"],chan);
214 }
215
216 tell_remote(string str)
217 {
218     llShout(REMOTE_CHANNEL,llXorBase64Strings(llStringToBase64((string)encryption + str), llStringToBase64((string)encryption)));
219 }
220
221 menu(key id)        //Dialog menus for all 3 modes
222 {
223     list buttons = [];
224     string title = "** Monitor control **";
225     
226     extendtimer();
227
228     if(mode != 0)
229     {
230         if(mode == 1)       //Pictures menu
231         {
232             title+="\n  Picture Viewer mode";
233             buttons+=["Browse"];
234             if(loop_image == FALSE)
235                 buttons+=["Loop"];
236             else
237                 buttons+=["Unloop"];
238             buttons+=["Video","Power off","Help","Fix scale"];
239         }
240         else                //Video menu
241         {
242             title+="\n Video display mode\n"+moviename+"\nTip:\nClick 'TV Guide' to view the Online bookmarks.";
243             buttons+=["Pictures","Configure","Power off","Loop","Unload","Help","Play","Stop","Pause","TV Guide","Bookmarks","Set URL"];
244         }
245     }
246     else
247         buttons += ["Pictures","Video","Help"];
248     
249     llDialog(id,title,buttons,chan);
250 }
251
252 display_texture(integer check)  //Display texture and set name in description (picture mode)
253 {                               //"Check" holds the number of textures in contents. The function uses "current_texture" to display.
254     string name = llGetInventoryName(INVENTORY_TEXTURE,current_texture);
255     llSetTexture(name,DISPLAY_ON_SIDE);
256     report("Showing picture: "+name+" ("+(string)(current_texture+1)+"/"+(string)check+")");
257 }
258     
259
260 next()  //Change to next texture (picture mode)
261 {       //This function is used twice - by the menu and timer. Therefor, it is a dedicated function.
262     current_texture++;
263     integer check = llGetInventoryNumber(INVENTORY_TEXTURE);
264     if(check == 0)
265     {
266         llSetTexture(BLANK,DISPLAY_ON_SIDE);
267         current_texture = 0;
268         report("No pictures found.");
269         return;
270     }
271     if(check == current_texture)
272         current_texture = 0;
273     
274     display_texture(check);
275     return;
276 }
277
278 //////////////
279 // from hufflets...
280
281 // returns the index of the first occurrence of "pattern" inside
282 // the "full_string".  if it is not found, then a negative number is returned.
283 integer find_substring(string full_string, string pattern)
284 { return llSubStringIndex(llToLower(full_string), llToLower(pattern)); }
285
286 // returns TRUE if the "prefix" string is the first part of "compare_with".
287 integer is_prefix(string compare_with, string prefix)
288 { return find_substring(compare_with, prefix) == 0; }
289
290 //////////////
291 // huffware script: auto-retire, by fred huffhines, version 2.8.
292 // distributed under BSD-like license.
293 //   !!  keep in mind that this code must be *copied* into another
294 //   !!  script that you wish to add auto-retirement capability to.
295 // when a script has auto_retire in it, it can be dropped into an
296 // object and the most recent version of the script will destroy
297 // all older versions.
298 //
299 // the version numbers are embedded into the script names themselves.
300 // the notation for versions uses a letter 'v', followed by two numbers
301 // in the form "major.minor".
302 // major and minor versions are implicitly considered as a floating point
303 // number that increases with each newer version of the script.  thus,
304 // "hazmap v0.1" might be the first script in the "hazmap" script continuum,
305 // and "hazmap v3.2" is a more recent version.
306 //
307 // example usage of the auto-retirement script:
308 //     default {
309 //         state_entry() {
310 //            auto_retire();  // make sure newest addition is only version of script.
311 //        }
312 //     }
313 // this script is partly based on the self-upgrading scripts from markov brodsky
314 // and jippen faddoul.
315 //////////////
316 auto_retire() {
317     string self = llGetScriptName();  // the name of this script.
318     list split = compute_basename_and_version(self);
319     if (llGetListLength(split) != 2) return;  // nothing to do for this script.
320     string basename = llList2String(split, 0);  // script name with no version attached.
321     string version_string = llList2String(split, 1);  // the version found.
322     integer posn;
323     // find any scripts that match the basename.  they are variants of this script.
324     for (posn = llGetInventoryNumber(INVENTORY_SCRIPT) - 1; posn >= 0; posn--) {
325         string curr_script = llGetInventoryName(INVENTORY_SCRIPT, posn);
326         if ( (curr_script != self) && (llSubStringIndex(curr_script, basename) == 0) ) {
327             // found a basic match at least.
328             list inv_split = compute_basename_and_version(curr_script);
329             if (llGetListLength(inv_split) == 2) {
330                 // see if this script is more ancient.
331                 string inv_version_string = llList2String(inv_split, 1);  // the version found.
332                 // must make sure that the retiring script is completely the identical basename;
333                 // just matching in the front doesn't make it a relative.
334                 if ( (llList2String(inv_split, 0) == basename)
335                     && ((float)inv_version_string < (float)version_string) ) {
336                     // remove script with same name from inventory that has inferior version.
337                     llRemoveInventory(curr_script);
338                 }
339             }
340         }
341     }
342 }
343 //
344 // separates the base script name and version number.  used by auto_retire.
345 list compute_basename_and_version(string to_chop_up)
346 {
347     // minimum script name is 2 characters plus a version.
348     integer space_v_posn;
349     // find the last useful space and 'v' combo.
350     for (space_v_posn = llStringLength(to_chop_up) - 3;
351         (space_v_posn >= 2) && (llGetSubString(to_chop_up, space_v_posn, space_v_posn + 1) != " v");
352         space_v_posn--) {
353         // look for space and v but do nothing else.
354     }
355     if (space_v_posn < 2) return [];  // no space found.
356     // now we zoom through the stuff after our beloved v character and find any evil
357     // space characters, which are most likely from SL having found a duplicate item
358     // name and not so helpfully renamed it for us.
359     integer indy;
360     for (indy = llStringLength(to_chop_up) - 1; indy > space_v_posn; indy--) {
361         if (llGetSubString(to_chop_up, indy, indy) == " ") {
362             // found one; zap it.  since we're going backwards we don't need to
363             // adjust the loop at all.
364             to_chop_up = llDeleteSubString(to_chop_up, indy, indy);
365         }
366     }
367     string full_suffix = llGetSubString(to_chop_up, space_v_posn, -1);
368     // ditch the space character for our numerical check.
369     string chop_suffix = llGetSubString(full_suffix, 1, llStringLength(full_suffix) - 1);
370     // strip out a 'v' if there is one.
371     if (llGetSubString(chop_suffix, 0, 0) == "v")
372         chop_suffix = llGetSubString(chop_suffix, 1, llStringLength(chop_suffix) - 1);
373     // if valid floating point number and greater than zero, that works for our version.
374     string basename = to_chop_up;  // script name with no version attached.
375     if ((float)chop_suffix > 0.0) {
376         // this is a big success right here.
377         basename = llGetSubString(to_chop_up, 0, -llStringLength(full_suffix) - 1);
378         return [ basename, chop_suffix ];
379     }
380     // seems like we found nothing useful.
381     return [];
382 }
383 //
384 //////////////
385
386 default {
387     state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; }
388     on_rez(integer parm) { state rerun; }
389 }
390 state rerun { state_entry() { state default; } }
391
392 state real_default
393 {
394     state_entry()
395     {
396         auto_retire();
397         llListen(EXTERNAL_TOUCH_CHANNEL, "", NULL_KEY, "");
398             // we listen on our touch channel in all cases and in all states.  this allows us
399             // to always pass along the user's touch from other prims to run the menus.
400         chan = (integer)llFrand(1000) + 1000;   //Pick a random listen channel for the listener
401         if(PICTURE_ROTATION_TIMER <= 0)         //Ensure the value is no less or equal 0
402             PICTURE_ROTATION_TIMER = 1;
403         llListenRemove(listenHandle);
404         listenHandle = -1;
405         last_owner = llGetOwner();
406         groupcheck = llRequestAgentData(llGetOwner(),DATA_NAME);
407         off();
408         llOpenRemoteDataChannel();
409         // fred's changes to start up in picture viewing looper.
410         mode = 1;  // picture viewing.
411         pictures();  // show the pictures.
412         loop_image = TRUE;
413         llSetTimerEvent(PICTURE_ROTATION_TIMER);  // keep showing new pics.
414     }
415     
416     on_rez(integer i)
417     {
418         llSay(0,"Welcome to FreeView - your free, open-source television!");
419         llResetScript();
420     }
421
422     touch_start(integer total_number)
423     {
424         //-------------------------------------------------------------------------------
425         //Listen only to owner or group member. Edit this code to change access controls.
426         if(llDetectedKey(0) != llGetOwner() && llDetectedGroup(0) == FALSE)
427             return;
428         //-------------------------------------------------------------------------------
429
430         menu(llDetectedKey(0));
431     }
432     
433     changed(integer change)
434     {
435         if(change == CHANGED_INVENTORY) //If inventory change
436         {
437             if(mode == 1)   //If picture mode
438             {
439                 integer check = llGetInventoryNumber(INVENTORY_TEXTURE);
440                 if(check != 0)
441                 {
442                     current_texture = 0;
443                     display_texture(check);
444                 }
445                 else
446                 {
447                     llSetTexture(BLANK,DISPLAY_ON_SIDE);
448                     report("No pictures found.");
449                 }
450             }
451             else
452                 if(mode == 2)   //If video mode
453                     if(finditem(NOTECARD) != -1)    //And bookmarks notecard present
454                         if(notecardkey != llGetInventoryKey(NOTECARD))
455                             tempuser = llGetNumberOfNotecardLines(NOTECARD);    //Reload number of lines
456         }
457         else if(change == CHANGED_OWNER)
458         {
459             isGroup = TRUE;
460             last_owner = llGetOwner();
461             groupcheck = llRequestAgentData(llGetOwner(),DATA_NAME);
462             
463             if(mode == 2)
464             {
465                 llSay(0,"Detected change in ownership. Attempting to obtain current parcel media texture...");
466                 video();
467             }
468         }
469     }
470     
471     listen(integer channel, string name, key id, string message)
472     {
473         if ( (channel == EXTERNAL_TOUCH_CHANNEL) && is_prefix(message, "touched")) {
474             // pretend we got touched by the av.
475             message = llDeleteSubString(message, 0, 6);
476             menu((key)message);
477             return;
478         }        
479         if(message == "Pictures")
480         {
481             if(mode == 2)
482                 llParcelMediaCommandList([PARCEL_MEDIA_COMMAND_STOP]);
483             pictures();
484             mode = 1;
485             menu(id);
486             return;
487         }
488         if(message == "Video")
489         {
490             video();
491             mode = 2;
492             menu(id);
493             return;
494         }
495         if(message == "Power off")
496         {
497             if(mode == 2)
498                 llParcelMediaCommandList([PARCEL_MEDIA_COMMAND_UNLOAD]);
499             off();
500             mode = 0;
501             return;
502         }
503         if(message == "Help")
504         {
505             llSay(0,"Help documentation is available at: http://www.slguide.com/help");
506             if(isGroup)
507             {
508                 if(id == NULL_KEY)
509                 {
510                     llSay(0,"FreeView cannot load help pages while set to group without the remote.");
511                     llSay(0,"For further assistance, please consult: http://slguide.com/help");
512                 }
513                 else
514                     tell_remote("HELP"+(string)id+(string)XML_channel);
515             }
516             else
517                 llLoadURL(id,"Help pages for FreeView","http://www.slguide.com?c="+(string)XML_channel+"&help=1");
518         }
519         if(mode == 1)
520         {
521             if(message == "Browse")
522             {
523                 loop_image = FALSE;
524                 browse(id);
525                 return;
526             }
527             if(message == "Next")
528             {
529                 extendtimer();
530                 next();
531                 browse(id);
532             }
533             if(message == "Back")
534             {
535                 extendtimer();
536                 current_texture--;
537                 integer check = llGetInventoryNumber(INVENTORY_TEXTURE);
538                 if(check == 0)
539                 {
540                     llSetTexture(BLANK,DISPLAY_ON_SIDE);
541                     current_texture = 0;
542                     report("No pictures found.");
543                     return;
544                 }
545                 if(current_texture < 0)
546                     current_texture = check - 1;
547                 
548                 display_texture(check);
549                 
550                 browse(id);
551                 return;
552             }
553             if(message == "Menu")
554             {
555                 menu(id);
556                 return;
557             }
558             if(message == "Loop")
559             {
560                 llSetTimerEvent(PICTURE_ROTATION_TIMER);
561                 loop_image = TRUE;
562                 llOwnerSay("Picture will change every "+(string)PICTURE_ROTATION_TIMER+" seconds.");
563                 return;
564             }
565             if(message == "Unloop")
566             {
567                 loop_image = FALSE;
568                 llOwnerSay("Picture loop disabled.");
569                 return;
570             }
571             if(message == "Fix scale")
572             {
573                 llSay(0,"Setting display texture to 1,1 repeats and 0,0 offset.");
574                 llScaleTexture(1, 1, DISPLAY_ON_SIDE);
575                 llOffsetTexture(0, 0, DISPLAY_ON_SIDE);
576                 return;
577             }
578         }
579         if(mode == 2)
580         {
581             if(channel == REMOTE_CHANNEL)
582             {
583                 if(encryption == 0)
584                     encryption = (integer)message;
585                 llListenRemove(listenRemote);
586                 listenRemote = -1;
587                 llSay(0,"Remote configured ("+(string)id+")");
588             }
589                 
590             if(message == "TV Guide")
591             {
592                 if(isGroup)
593                 {
594                     if(!encryption)
595                     {
596                         llSay(0,"** Error - This FreeView object has been deeded to group. You must use a Remote control to open the TV Guide.");
597                         llSay(0,"You can set up the remote control from the Video -> Configuration menu. Please refer to the notecard for further assistance.");
598                         return;
599                     }
600                     tell_remote((string)id+(string)XML_channel+(string)llGetOwner());
601                 }
602                 else
603                     llLoadURL(id, "Come to the Guide to Start Your Viewer Playing!", "http://slguide.com/index.php?v=" + (string)llGetKey() + "&c=" + (string)XML_channel + "&o=" + (string)llGetOwner() + "&");
604                 return;
605             }
606
607             string header = "Video mode"+moviename+": ";
608             
609             if(message == "<< Prev")
610             {
611                 notecardline--;
612                 if(notecardline < 0)
613                     notecardline = numberofnotecardlines - 1;
614                 tempuser = id;
615                 llGetNotecardLine(NOTECARD,notecardline);
616                 return;
617             }
618             if(message == "Next >>")
619             {
620                 notecardline++;
621                 if(notecardline >= numberofnotecardlines)
622                     notecardline = 0;
623                 tempuser = id;
624                 llGetNotecardLine(NOTECARD,notecardline);
625                 return;
626             }
627             if(message == "Use")
628             {
629                 if(tempurl == "** No URL specified! **")
630                     tempurl = "";
631                 seturl(tempurl,id);
632                 return;
633             }
634                     
635             if(message == "Menu")
636             {
637                 menu(id);
638                 return;
639             }
640             if(message == "Configure")
641             {
642                 config(id);
643                 return;
644             }
645             if(message == "Bookmarks")
646             {
647                 if(notecardcheck != -1)
648                 {
649                     llDialog(id,"Error: No valid bookmark data found in notecard '"+NOTECARD+"'.",["Menu"],chan);
650                     return;
651                 }
652                 if(finditem(NOTECARD) != -1)                
653                 {
654                     tempuser = id;
655                     if(numberofnotecardlines < notecardline)
656                         notecardline = 0;
657                     llGetNotecardLine(NOTECARD,notecardline);
658                 }
659                 else
660                     llDialog(id,"Error: No notecard named "+NOTECARD+" found in contents.",["Menu"],chan);
661                 return;
662             }
663             
664             if(llGetLandOwnerAt(llGetPos()) != llGetOwner())    //If we do not have permissions to actually do the following functions
665             {
666                 llSay(0,"Error: Cannot modify parcel media settings. "+llGetObjectName()+" is not owned by parcel owner.");
667                 menu(id);
668                 return; //Abort
669             }
670             
671             if(listenUrl != -1 && channel == 1) //Incoming data from "Set URL" command (user spoke on channel 1)
672             {
673                 llListenRemove(listenUrl);
674                 listenUrl = -1;
675                 tempmoviename = "";
676                 seturl(message,id);
677             }
678             if(message == "Play")
679             {
680                 report(header+"Playing");
681                 llParcelMediaCommandList([PARCEL_MEDIA_COMMAND_PLAY]);
682                 return;
683             }
684             if(message == "Stop")
685             {
686                 report(header+"Stopped");
687                 llParcelMediaCommandList([PARCEL_MEDIA_COMMAND_STOP]);
688                 return;
689             }
690             if(message == "Pause")
691             {
692                 report(header+"Paused");
693                 llParcelMediaCommandList([PARCEL_MEDIA_COMMAND_PAUSE]);
694                 return;
695             }
696             if(message == "Unload")
697             {
698                 report(header+"Stopped");
699                 llParcelMediaCommandList([PARCEL_MEDIA_COMMAND_UNLOAD]);
700                 return;
701             }
702             if(message == "Loop")
703             {
704                 llParcelMediaCommandList([PARCEL_MEDIA_COMMAND_LOOP]);
705                 return;
706             }
707             //URL , Auto-Scale, 
708             if(message == "Set URL")
709             {
710                 report(header+"Stopped");
711                 listenUrl = llListen(1,"",id,"");
712                 llDialog(id,"Please type the URL of your choice with /1 in thebegining. For example, /1 www.google.com",["Ok"],938);
713                 return;
714             }
715             if(message == "Align ON")
716             {
717                 report(header+"Stopped");
718                 llParcelMediaCommandList([PARCEL_MEDIA_COMMAND_AUTO_ALIGN,TRUE]);
719                 menu(id);
720                 return;
721             }
722             if(message == "Align OFF")
723             {
724                 report(header+"Stopped");
725                 llParcelMediaCommandList([PARCEL_MEDIA_COMMAND_AUTO_ALIGN,FALSE]);
726                 menu(id);
727                 return;
728             }
729             if(message == "Set Remote")
730             {
731                 llSay(0,"Configuring remote...");
732                 encryption = 0;
733                 llListenRemove(listenRemote);
734                 listenRemote = llListen(REMOTE_CHANNEL,"","","");
735                 llSay(REMOTE_CHANNEL,"SETUP");
736             }
737         }
738     }
739     
740     dataserver(key queryid, string data)
741     {
742         if(queryid == groupcheck)       //Test if object is deeded to group
743         {
744             groupcheck = NULL_KEY;
745             isGroup = FALSE;
746             return;
747         }
748         
749         if(queryid == tempuser) //If just checking number of notecard lines
750         {
751             numberofnotecardlines = (integer)data;
752             notecardkey = llGetInventoryKey(NOTECARD);
753             notecardcheck = 0;
754             llGetNotecardLine(NOTECARD,notecardcheck);
755             return;
756         }
757         if(notecardcheck != -1)
758         {
759             if(data != EOF)
760             {
761                 if(data == "")
762                 {
763                     notecardcheck++;
764                     llGetNotecardLine(NOTECARD,notecardcheck);
765                 }
766                 else
767                 {
768                     notecardcheck = -1;
769                     return;
770                 }
771             }
772             else
773                 return;
774         }
775
776         if(data == "" && notecardline < numberofnotecardlines)    //If user just pressed "enter" in bookmarks, skip
777         {
778             notecardline++;
779             llGetNotecardLine(NOTECARD,notecardline);
780             return;
781         }
782         
783         if(data == EOF)
784         {
785             notecardline = 0;
786             llGetNotecardLine(NOTECARD,notecardline);
787             return;
788         }
789         list parsed = llParseString2List(data,["|","| "," |"," | "],[]);    //Ensure no blank spaces before "http://".
790         string name = llList2String(parsed,0);
791         tempurl = llList2String(parsed,1);
792         if(tempurl == "")
793             tempurl = "** No URL specified! **";
794             
795         tempmoviename = name;
796                 
797         llDialog(tempuser,"Bookmarks notecard ("+(string)(notecardline+1)+"/"+(string)numberofnotecardlines+")\n"+name+" ("+mediatype(llList2String(llParseString2List(tempurl,["."],[]),-1))+")\n"+tempurl,["<< Prev","Use","Next >>","Menu"],chan);
798     }
799     
800     remote_data(integer type, key channel, key message_id, string sender, integer ival, string sval)
801     {
802         if (type == REMOTE_DATA_CHANNEL)
803         {
804             XML_channel = channel;
805         } 
806         else if(type == REMOTE_DATA_REQUEST)
807         {
808             list media_info = llParseString2List(sval, ["|"], []);
809             tempmoviename = llList2String(media_info,0);
810             seturl(llList2String(media_info,1),NULL_KEY);
811             llRemoteDataReply(channel, message_id, sval, 1);
812         }
813     }
814     
815     timer()
816     {
817         if(llGetTime() > listenTimer)       //If listener time expired...
818         {
819             llListenRemove(listenHandle);   //Remove listeneres.
820             llListenRemove(listenUrl);
821             llListenRemove(listenRemote);
822             listenHandle = -1;
823             listenUrl = -1;
824             listenRemote = -1;
825             listenTimer = -1;
826             if(loop_image == FALSE || mode != 1) //If we're not looping pictures or are in picture mode at all
827                 llSetTimerEvent(0.0);   //Remove timer
828         }
829         
830         if(loop_image == TRUE && mode == 1) //If we're looping pictures and and we're in picture mode...
831             next(); //Next picture
832     }
833 }
834