| Furre Online Class |
| Written by Dream Dancer | |
| Aug 20, 2010 at 01:28 PM | |
|
Various details of the Furre Online Class. One of the things to bear in mind as you read this is that as I develop the project, the actual class may differ between this and what is actually used in the program, always refer to the program for the latest version of this class. This is one of two main class's used by the project to manage the items that make up the whole online list, Furre's and dreams are handled by similar systems, with adjustments made to each to account for slight differences between the two. Everyone has a different way of coding, one can group calls by the overall function they do, (for example AddFurre could be followed by RemoveFurre), or alphabetically, which is how I tend to do things. To start off, lets get the structure used by the internal array for the class: TYPE FURRESTRUCTURE FurreName AS ASCIIZ * 64 Notes AS ASCIIZ * 64 RecordIndex AS LONG GroupMember AS LONG SoundOnline AS LONG SoundOffline AS LONG SoundSays AS LONG SoundWhispers AS LONG lastOnline AS QUAD lastWhispered AS ASCIIZ * 64 ShortName AS ASCIIZ * 64 TreeNode AS DWORD OnlineStatus AS LONG FreshOnLine AS LONG Unknown AS LONG InQuery AS LONG END TYPE GeSHi parsed in 0.0690159797668 seconds. With that out the way, the class manages access to the various elements of the array in some parts, other parts return the whole Furre structure to the caller, for example when checking for fresh Furre's coming online or going offline, much easier to deal with the structure at the caller rather than making numerous calls on the class. On class creation, need to define the array, and initialize it, along with other elements of the class. When I create class's, I've always included the DESTROY method anyway just for completeness sake even if I never actually do anything with it. Well, when initially creating the class, I put in a debug print that tells me the class was destroyed, ... ' furre online class manager CLASS FurreOnlineClass ' furre management INSTANCE FurreList() AS FURRESTRUCTURE INSTANCE RecordCount AS LONG INSTANCE LargeList AS LONG INSTANCE LargeIndex AS LONG INSTANCE FirstFetch AS LONG CLASS METHOD CREATE() DIM FurreList(0) ' $DeleteFurre is a constant FurreList(0).FurreName = $DeleteFurre RecordCount = 1000 LargeList = %FALSE LargeIndex = 0 FirstFetch = %FALSE END METHOD CLASS METHOD DESTROY() END METHODGeSHi parsed in 0.0799360275269 seconds. Now we get into the interface itself of the class. There used to be a couple of private method's to the class which did sorting and shortname generation for the class when dealing with the online check string, those were removed when I converted the system to work with large query's. INTERFACE FurreOnlineManager INHERIT IUNKNOWNGeSHi parsed in 0.0115480422974 seconds. Every name in the system contains both the Properly Cased Name and the shortname for each entry. So whenever I call on the class to add someone to the list, there's checks to see if the name exists in the array by both forms of the name. Any time I check for the presence of an element in the array I take advantage of PowerBasic's strong language feature of Array Scan. This can scan numeric arrays, strings, and structures, though when dealing with structures, it's treated as though it was a string. This necessitates limiting the starting point and width of the search to keep the scan within the boundary of the element that you're scanning. If the name is not found in .FurreName of the array, the fail check, (by a return of Zero), does a scan of the shortname. If either is found, the method bails to ensure duplicate entries are not created, Array Scan returns the Index+1 of the found element, so a scan that returns a Zero means we can add the Furre.
METHOD AddFurre(Furre AS FURRESTRUCTURE, DoPurge AS LONG) LOCAL fIndex, sLen, sStart AS LONG LOCAL fName, sName AS STRING ' prevent duplicates fName = Furre.FurreName sName = fName & $NUL sLen = LEN(sName) ARRAY SCAN FurreList(), _ COLLATE UCASE, FROM 1 TO sLen, _ = sName, TO fIndex IF (fIndex = 0) THEN ' check shortnames ' MakeShortName is a MACRO sName = MakeShortName(fName) & $NUL ' do this JIC ' note that we can scan elements from the MIDDLE of the structure ' by providing an offset into the structure sStart = %ShortNameIndex sLen = LEN(sName) + sStart - 1 ARRAY SCAN FurreList(), _ COLLATE UCASE, FROM %ShortNameIndex TO sLen, _ = sName, TO fIndex END IF IF (fIndex = 0) THEN fIndex = UBOUND(FurreList) + 1 REDIM PRESERVE FurreList(fIndex) FurreList(fIndex) = Furre FurreList(fIndex).ShortName _ = MakeShortName(FurreList(fIndex).FurreName) FurreList(fIndex).RecordIndex = RecordCount INCR RecordCount IF (ISTRUE DoPurge) THEN me.Purge() END IF END IF END METHODGeSHi parsed in 0.148969888687 seconds. AddNewFurre is different from above, it's a fast call to quickly add a furre to the list for when either you whisper someone from the chat buffer, or someone whispers you. We pass it a few items from the caller, like the name, any notes, and the name of the Local Client Alt which generated the action. To understand that last part requires a knowledge of the CopyData system. This is also used by the Add To Pounce action to add a name to the list when it doesn't exist in the system, the CopyData handler then immediately calls ConvertUnKnown to, eh, convert the unknown furre into a permanent member.
METHOD AddNewFurre(fName AS STRING, _ fNotes AS STRING, fWho AS STRING) AS LONG ' fWho is which alt whispered the name, if any LOCAL Furre AS FURRESTRUCTURE LOCAL iResult AS LONG Furre.FurreName = fName Furre.Notes = fNotes Furre.RecordIndex = -1 ' indicator it's a new record Furre.GroupMember = 0 Furre.SoundOnline = 1 Furre.SoundOffline = 0 Furre.SoundWhispers = 1 Furre.SoundSays = 0 Furre.lastOnline = 0 Furre.lastWhispered = fWho Furre.TreeNode = 0 ' set them offline, things work out Furre.OnlineStatus = 0 ' 1 Furre.FreshOnLine = %FreshCount Furre.Unknown = %UnknownExpires me.AddFurre(Furre, %TRUE) METHOD = me.FurreIndex(fName) END METHODGeSHi parsed in 0.0970258712769 seconds. Check large is a means to interrogate the class and get the status of the list manager, if it comes back true, from either the Furre or Dream class, then the program tampers with the image of the Menu toolbar button.
PROPERTY GET CheckLarge() AS LONG PROPERTY = ISTRUE LargeList END PROPERTYGeSHi parsed in 0.0193071365356 seconds. AddToPounce is a handler for when the command from the client tells pounce to add the name to the permanent list.
METHOD ConvertUnknown(fIndex AS LONG) AS LONG LOCAL ImgFlag, bFlag AS DWORD LOCAL iResult, fStatus AS LONG LOCAL fName AS STRING IF (fIndex >= 0) AND (fIndex <= UBOUND(FurreList)) THEN FurreList(fIndex).Unknown = -1 FurreList(fIndex).Notes = "" ' do not force them offline, bad behavior IF (FurreList(fIndex).TreeNode <> 0) THEN ' MakeFurreImage is a macro for selecing correct image ImgFlag = MakeFurreImage(FurreList(fIndex).OnlineStatus) ' FurreOnlineStatus creates a sort index for the name fStatus = FurreOnlineStatus(fIndex, _ FurreList(fIndex).OnlineStatus) ' check if they're a fresh online and bold if needed bFlag = IIF(FurreList(fIndex).FreshOnline > 0, %TVIS_BOLD, 0) fName = FurreList(fIndex).FurreName ' UpdateTreeItem is a macro to keep it simple here iResult = UpdateTreeItem(fName,_ FurreList(fIndex).TreeNode,bFlag,ImgFlag,fStatus) iResult = %TRUE ELSE iResult = %FALSE END IF END IF METHOD = iResult END METHODGeSHi parsed in 0.128350019455 seconds. FirstCheck is a means to keep the program from spamming clients with a bunch of "logged on" messages if Faux Pawn is started after the client.
' FirstCheck is used by the notification system, ' can we start spamming you? PROPERTY GET FirstCheck() AS LONG PROPERTY = ISTRUE FirstFetch END PROPERTYGeSHi parsed in 0.0193700790405 seconds. FreshCheck was used at one point in examining the status of an entry to see if the person just came online, been depreciated but not removed. ' get the value of FreshOnline for bold checking PROPERTY GET FreshCheck(fIndex AS LONG) AS LONG LOCAL Work AS STRING IF (fIndex >= 0) AND (fIndex <= UBOUND(FurreList)) THEN PROPERTY = FurreList(fIndex).FreshOnLine END IF END PROPERTYGeSHi parsed in 0.0441799163818 seconds. One of the ideas I had was to check for fresh onlines within a group and bold the group name, for some reason it failed to work as expected. Will probably look at this later.
PROPERTY GET FreshCount(gIndex AS LONG) AS LONG LOCAL fIndex, gCount AS LONG gCount = 0 FOR fIndex = 0 TO UBOUND(FurreList) IF (FurreList(fIndex).GroupMember = gIndex) THEN IF (FurreList(fIndex).FreshOnLine > 0) THEN INCR gCount END IF NEXT fIndex PROPERTY = gCount END PROPERTYGeSHi parsed in 0.0524621009827 seconds. FreshScan is called every 10 seconds, so any values used in the count down timers is one tenth the expected time for the expirations of the values. FreshScan handles both the clearing the bold status of a "new" entry as well as timing out Furre's who were added to the list due to a whisper.
METHOD FreshScan() AS LONG LOCAL fIndex, fResult AS LONG LOCAL fStatus, iResult AS LONG LOCAL fName AS STRING ' if we find ANY which hit zero, we return TRUE fResult = %FALSE FOR fIndex = 0 TO UBOUND(FurreList) IF (FurreList(fIndex).FreshOnLine > 0) THEN DECR FurreList(fIndex).FreshOnLine ' update the tree from here, easier to manage IF (FurreList(fIndex).FreshOnLine = 0) THEN IF (FurreList(fIndex).TreeNode <> 0) THEN ' FurreOnlineStatus creates a sort index for the name fStatus = FurreOnlineStatus(fIndex,_ FurreList(fIndex).OnlineStatus) ' MakeFurreListEntry is for seeing if notes exist ' and tacking that onto the TreeList string fName = MakeFurreListEntry(FurreList(fIndex)) ' ClearBold is a macro ClearBold(fName,FurreList(fIndex).TreeNode,fStatus) fResult = %TRUE END IF END IF END IF IF (FurreList(fIndex).Unknown > 0) THEN DECR FurreList(fIndex).Unknown ' delete the tree item from here, easier to manage IF (FurreList(fIndex).Unknown = 0) THEN IF (FurreList(fIndex).TreeNode <> 0) THEN ' below is a macro that sets up the call to the API iResult = TreeView_DeleteItem(hTreeView, _ FurreList(fIndex).TreeNode) END IF ' set the name to $DeleteFurre for purging FurreList(fIndex).FurreName = $DeleteFurre fResult = %TRUE END IF END IF NEXT fIndex ' admittedly, no reason to call Purge if you only debolded names, ... IF (ISTRUE fResult) THEN me.Purge() ' return the results to the caller so caller can call the function ' which rebuilds the treeview's group counts METHOD = fResult END METHODGeSHi parsed in 0.215377092361 seconds. The dual properties for getting and setting the whole Furre structure is because when I'm scanning the list, either for new onlines in LoadFreshFurres or with the user menu triggered event of changing who is shown on the list via LoadFurreList, it is simpler to have the whole structure in the processing of the nodes over several calls on the class for each iteration. The SET property is included, but I consider it a tad dangerous because I don't do any verification that the values are clean and safe for the system, there should be checking involved.
' property calls to get and set the whole furre structure as is ' nasty property, nothing to protect any 'private' values PROPERTY GET Furre(fIndex AS LONG) AS FURRESTRUCTURE IF (fIndex >= 0) AND (fIndex <= UBOUND(FurreList)) THEN PROPERTY = FurreList(fIndex) END IF END PROPERTY PROPERTY SET Furre(fIndex AS LONG, nFurre AS FURRESTRUCTURE) IF (fIndex >= 0) AND (fIndex <= UBOUND(FurreList)) THEN FurreList(fIndex) = nFurre END IF END PROPERTYGeSHi parsed in 0.0647749900818 seconds. Obviously, when I need to iterate through the list, I need the count of members in the list.
' just how many furre's are on the list? PROPERTY GET FurreArrayCount() AS LONG LOCAL fCount AS LONG fCount = UBOUND(FurreList) ' note that if there is none, we ensure we return Zero IF (fCount = 0) AND (FurreList(0).UnKnown = 0) THEN DECR fCount PROPERTY = fCount END PROPERTYGeSHi parsed in 0.0431580543518 seconds. FurreIndex is a combination value, the property never returns Zero for a member who is in the list, Zero means that the name was not found in the list. To ensure it doesn't return Zero for someone who is in the list it always checks ShortName as well. If the name is in the list, it decrements the value returned by the Array Scan, and then OR's it with the status of the entry, the values of the constants %OnlineFurre or %OfflineFurre. Those two values were selected to aid the sorting of the list so that online names are before offline names. The second part of the sort order is from the index value itself so the list is alphabetized.
PROPERTY GET FurreIndex(fName AS STRING) AS LONG LOCAL fIndex AS LONG LOCAL sName AS STRING LOCAL sLen, sStart AS LONG sName = fName & $NUL sLen = LEN(sName) ARRAY SCAN FurreList(), _ COLLATE UCASE, FROM 1 TO sLen, _ = sName, TO fIndex IF (fIndex = 0) THEN ' check shortnames sName = MakeShortName(fName) & $NUL ' do this JIC ' note that we can scan elements from the MIDDLE of the structure ' by providing an offset into the structure sStart = %ShortNameIndex sLen = LEN(sName) + sStart - 1 ARRAY SCAN FurreList(), _ COLLATE UCASE, FROM %ShortNameIndex TO sLen, _ = sName, TO fIndex END IF IF fIndex > 0 THEN DECR fIndex fIndex = fIndex OR IIF(ISTRUE FurreList(fIndex).OnlineStatus, _ %OnlineFurre, %OfflineFurre) END IF PROPERTY = fIndex END PROPERTYGeSHi parsed in 0.123426914215 seconds. FurreName is used with several parts of the program when the name of a selected Furre is selected from the list, and while I also got a means to retrieve the shortname from the system, don't think I actually use it anyhere. Do note that any time I call on the system to retrieve a value from the class by index, there's an error check on the index requested to ensure that it's within the bounds of the array to prevent an error from crashing the program with that dreadful error message "Index out of bounds".
PROPERTY GET FurreName(fIndex AS LONG) AS STRING IF (fIndex >= 0) AND (fIndex <= UBOUND(FurreList)) THEN PROPERTY = FurreList(fIndex).FurreName END IF END PROPERTY PROPERTY GET FurreShortName(fIndex AS LONG) AS STRING IF (fIndex >= 0) AND (fIndex <= UBOUND(FurreList)) THEN PROPERTY = FurreList(fIndex).ShortName END IF END PROPERTYGeSHi parsed in 0.062019109726 seconds. FurreWhispers is another method called by the CopyDataHandler when it gets the message from the client, we know the local name because the caller includes their name in the message in addition to who whispered them, or who they whispered. If the name is not found in the list, it's added as a temporary name.
METHOD FurreWhispers(fName AS STRING, FromTo AS STRING) AS LONG LOCAL fIndex, sLen AS LONG LOCAL sName AS STRING LOCAL Furre AS FURRESTRUCTURE ' scan the list both in furrelist and shortname list sName = fName & $NUL sLen = LEN(sName) ARRAY SCAN FurreList(), _ COLLATE UCASE, FROM 1 TO sLen, _ = sName, TO fIndex IF (fIndex = 0) THEN sName = MakeShortName(fName) & $NUL ' do this JIC sLen = LEN(sName) + %ShortNameIndex - 1 ARRAY SCAN FurreList(), _ COLLATE UCASE, FROM %ShortNameIndex TO sLen, _ = sName, TO fIndex END IF IF (fIndex = 0) THEN me.AddNewFurre(fName, "Whispered @ "& TIME$, FromTo) ELSE DECR fIndex FurreList(fIndex).lastWhispered = FromTo END IF METHOD = IIF(fIndex = 0, %TRUE, %FALSE) END METHODGeSHi parsed in 0.132568120956 seconds. GroupCount is uses strictly by the two functions which update the TreeList to produce the title for the group so it includes the number of online Furre's in a group as well as the total number in the group. (And there's a similiar set of calls for the DreamOnlineClass which returns dream counts.)
PROPERTY GET GroupCount(gIndex AS LONG) AS LONG LOCAL fIndex, gCount AS LONG gCount = 0 FOR fIndex = 0 TO UBOUND(FurreList) IF (FurreList(fIndex).GroupMember = gIndex) _ AND (FurreList(fIndex).Unknown <> 0) THEN INCR gCount NEXT fIndex PROPERTY = gCount END PROPERTYGeSHi parsed in 0.0493497848511 seconds. GroupDelete does what it says, sort of. If the DeleteGroup module passes it -1 for nGroup, the user requested that the members of the group also be deleted, otherwise nGroup is the new group which the members of the group are being transfered to.
' if we pass -1 to this function, should DELETE the members ' will need to delete the treenode if it exists as well METHOD GroupDelete(fGroup AS LONG, nGroup AS LONG) AS LONG LOCAL fIndex, fAffected AS LONG fAffected = 0 FOR fIndex = 0 TO UBOUND(FurreList) IF FurreList(fIndex).GroupMember = fGroup THEN IF (nGroup < 0) THEN ' deleting members FurreList(fIndex).FurreName = $DeleteFurre FurreList(fIndex).Unknown = 0 INCR fAffected ELSE ' just deleting the group FurreList(fIndex).GroupMember = nGroup FurreList(fIndex).TreeNode = 0 INCR fAffected END IF END IF NEXT fIndex METHOD = fAffected END METHODGeSHi parsed in 0.0838890075684 seconds. LastWhispered is used specifically by the DoubleClickManager, if the list entry has a name in the lastWhispered field, and the DoubleClickManager finds that name is connected, the system throws a %WhisperToWho message at that client. Otherwise it will send the message to the First client it finds in the ClientManager. If there are no clients connected, it calls on the Launcher to start a session.
PROPERTY GET LastWhispered(fIndex AS LONG) AS STRING IF (fIndex >= 0) AND (fIndex <= UBOUND(FurreList)) THEN PROPERTY = FurreList(fIndex).lastWhispered END IF END PROPERTYGeSHi parsed in 0.0326719284058 seconds. OnlineCheckString is magic. This is called by the thread which handles the generation of a query of the server for a group of names to be checked for their status. Rather than trying to be wonky and cache'ing the name string, it's just as fast to generate the list each time, avoids having to do additional checks on the existing string to see if it's changed. Three magic variables to the class are dealt with here, LargeList is True when the list is bigger than the internally set buffer size, LargeIndex is the index into the list where it's supposed to start, and finally FirstFetch is set once the system has run through the list at least once. As each name is gotten into the query string, the member .InQuery is set to be True, and .OnlineStatus is adjusted to faciliate the modification of their status later when the server query thread calls on a method further down that adjusts their status.
' this is where we should be checking on blowing 8k and split things PROPERTY GET OnlineCheckString() AS STRING LOCAL cString, Work, kString() AS STRING LOCAL fIndex, tLength, fLength AS LONG LOCAL fTotal AS LONG IF FurreList(0).FurreName = $DELETEFURRE THEN PROPERTY = "" EXIT PROPERTY END IF ' step through the list until buffer limit reached ' if entire list fits into buffer, LargeList = %FALSE LargeList = %FALSE fTotal = 0 tLength = 0 IF LargeIndex > UBOUND(FurreList) THEN LargeIndex = 0 FirstFetch = %TRUE END IF FOR fIndex = LargeIndex TO UBOUND(FurreList) Work = "u[]="& FurreList(fIndex).ShortName fLength = LEN(Work) + 1 ' account for the & tLength = tLength + fLength IF (tLength > %MAXFURRESIZE) THEN ' exceeded our available space, set largelist LargeList = %TRUE EXIT FOR END IF REDIM PRESERVE kString(fTotal) kString(fTotal) = Work INCR fTotal FurreList(fIndex).OnlineStatus _ = IIF(FurreList(fIndex).OnlineStatus = 1, 2, 0) FurreList(fIndex).InQuery = %TRUE NEXT fIndex cString = JOIN$(kString(), "&") fLength = LEN(cString) Work = TIME$ &" Chunk "& FORMAT$(LargeIndex) &"-"& FORMAT$(fIndex - 1) Work = Work &" Size="& FORMAT$(fLength) Work = Work &" (Total "& FORMAT$(UBOUND(FurreList)) &")" IF (fIndex <= UBOUND(FurreList)) OR (LargeIndex > 0) THEN LargeList = %TRUE END IF LargeIndex = fIndex Work = Work &" FirstFetch="& IIF$(ISTRUE FirstFetch, "TRUE", "FALSE") ' this only gets compiled if the constant is defined, for tracking the chunks #IF %DEF(%DEBUGQUERYCHUNKS) LOCAL FileOut AS LONG LOCAL FileName AS STRING FileName = EXE.PATH$ &"FetchString.txt" OPEN FileName FOR APPEND AS FileOut PRINT #FileOut, "Furres "& TIME$ &" "& Work CLOSE #FileOut #ENDIF PROPERTY = cString END PROPERTYGeSHi parsed in 0.273303031921 seconds. OnlineCount is the other side of the value generated by the functions used to manage the TreeList when it's updating the Group Names in the TreeList.
PROPERTY GET OnlineCount(gIndex AS LONG) AS LONG LOCAL fIndex, gCount AS LONG gCount = 0 FOR fIndex = 0 TO UBOUND(FurreList) IF (FurreList(fIndex).GroupMember = gIndex) THEN IF (FurreList(fIndex).OnlineStatus > 0) THEN INCR gCount END IF NEXT fIndex PROPERTY = gCount END PROPERTYGeSHi parsed in 0.0579309463501 seconds. OnlineStatus is manipulated from several points in the system, most places it is interrogated to check if the selected name is online, and in one place it's set to either On or Off, LoadFreshFurres. It's also set to Online when a client who has been notified of a Furre's existence in the list speaks, and the client informs the program that the Furre is online, and the program finds the name is listed offline, ...
PROPERTY GET OnlineStatus(fIndex AS LONG) AS LONG IF (fIndex >= 0) AND (fIndex <= UBOUND(FurreList)) THEN PROPERTY = FurreList(fIndex).OnlineStatus END IF END PROPERTY PROPERTY SET OnlineStatus(fIndex AS LONG, nStatus AS LONG) IF (fIndex >= 0) AND (fIndex <= UBOUND(FurreList)) THEN FurreList(fIndex).OnlineStatus = nStatus END IF END PROPERTYGeSHi parsed in 0.0706789493561 seconds. Purge is called when ever there's a change to the list that may include the deletion of a member of the list, a member is deleted by setting the name to a preset value of $DeleteFurre, which contains non-printable characters, specifically 0x0A as the first character. This aids the routine by permitting the sorting of the list in descending order, (remember that the FurreName is the first item in the structure), and scanning to the end for the special value. There's checks at the end to ensure that the method does not attempt to redim the array with a negative index, that would make bad things happen.
METHOD Purge() AS LONG LOCAL fIndex, fCount, fTotal AS LONG LOCAL fStart, sLen AS LONG LOCAL iResult AS LONG LOCAL sName AS STRING LOCAL Blank AS FURRESTRUCTURE ' sort the array inverse to put the deletions at the top ARRAY SORT FurreList(), COLLATE UCASE, DESCEND ' see if we have any to delete, cut down on processing time sName = $DeleteFurre & $NUL sLen = LEN(sName) ARRAY SCAN FurreList(), _ COLLATE UCASE, FROM 1 TO sLen, _ = sName, TO fStart fTotal = UBOUND(FurreList) IF (fStart = 0) THEN ' restore normal order ARRAY SORT FurreList(), COLLATE UCASE iResult = %FALSE ELSE ' else deal with deleted furres, in theory, only one at a time DECR fStart fCount = fTotal FOR fIndex = fStart TO fCount ' purge anyone with 0 in Unknown ' IF FurreList(fIndex).Unknown = 0 THEN IF FurreList(fIndex).FurreName = $DeleteFurre THEN DECR fTotal ' set the whole structure to the blank template FurreList(fIndex) = Blank END IF ' END IF NEXT fIndex ' prevent attempting to redim with negative index IF fTotal < 0 THEN REDIM FurreList(0) FurreList(0).FurreName = $DeleteFurre FurreList(0).Unknown = 0 FurreList(0).ShortName = "" ELSE REDIM PRESERVE FurreList(fTotal) END IF ' does nothing, in the code for debugging check fCount = UBOUND(FurreList) iResult = %TRUE END IF ARRAY SORT FurreList(), COLLATE UCASE METHOD = iResult END METHODGeSHi parsed in 0.21294093132 seconds. PurgeTemporary is called by the user selecting the menu selection for that purpose, it's to permit people with big lists that are not into large list mode getting the program out of large list mode when a few extra names cause it to enter that mode. Or just to dump all the extra names from the list without having to right click and delete each one.
METHOD PurgeTemporary() AS LONG LOCAL fIndex, fResult AS LONG LOCAL fStatus, iResult AS LONG LOCAL fName AS STRING ' if we find ANY which hit zero, we return TRUE fResult = %FALSE FOR fIndex = 0 TO UBOUND(FurreList) IF (FurreList(fIndex).Unknown > 0) THEN IF (FurreList(fIndex).TreeNode <> 0) THEN iResult = TreeView_DeleteItem _ (hTreeView, FurreList(fIndex).TreeNode) END IF FurreList(fIndex).FurreName = $DeleteFurre fResult = %TRUE END IF NEXT fIndex IF (ISTRUE fResult) THEN me.Purge() METHOD = fResult END METHODGeSHi parsed in 0.0894889831543 seconds. The property to Set the Query status for a member of the list is called by LoadFreshFurres to clear the status from the class as the name is checked in the function for their new status.
PROPERTY SET Query(fIndex AS LONG, nQuery AS LONG) IF (fIndex >= 0) AND (fIndex <= UBOUND(FurreList)) THEN FurreList(fIndex).InQuery = nQuery END IF END PROPERTYGeSHi parsed in 0.0358729362488 seconds. Each entry in the list is supposed to have a unique value assigned to it, I'm using a long value, which while I'm starting at 1000, still leaves the availability of a rather large range for use. Like 2,147,483,647. You'd have to be running the program for an awfully long time to roll that over, with LOTS of temp action.
PROPERTY GET RecordIndex(fName AS STRING) AS LONG LOCAL fIndex, sLen AS LONG LOCAL sName AS STRING sName = fName & $NUL sLen = LEN(sName) ARRAY SCAN FurreList(), _ COLLATE UCASE, FROM 1 TO sLen, _ = sName, TO fIndex IF fIndex > 0 THEN DECR fIndex fIndex = FurreList(fIndex).RecordIndex END IF PROPERTY = fIndex END PROPERTYGeSHi parsed in 0.0639450550079 seconds. RemoveFurre is only called when the menu action to delete a name from the list is used. If it's a temporary name, it happens immediately. If the menu action finds that .UnKnown is less than Zero, (the means by which the class tracks permanent members), the program pops up a "Are you sure" dialog.
METHOD RemoveFurre(fIndex AS LONG) AS LONG LOCAL iResult AS LONG ' returns %TRUE if removal is successfull iResult = %FALSE IF (fIndex >= 0) AND (fIndex <= UBOUND(FurreList)) THEN FurreList(fIndex).FurreName = $DeleteFurre FurreList(fIndex).Unknown = 0 iResult = me.Purge() END IF METHOD = iResult END METHODGeSHi parsed in 0.0553050041199 seconds. ResetQuery is called by the online query thread, if it fails to connect to the server, it needs to reset the query status of all the members which were included in the check string, and resets the pointer into the list.
METHOD ResetQuery() AS LONG LOCAL fIndex AS LONG FOR fIndex = UBOUND(FurreList) TO 0 STEP -1 IF (ISTRUE FurreList(fIndex).InQuery) THEN FurreList(fIndex).InQuery = %FALSE LargeIndex = fIndex END IF FurreList(fIndex).OnlineStatus _ = IIF(FurreList(fIndex).OnlineStatus > 0, 1, 0) NEXT fIndex IF (LargeIndex < 0) THEN LargeIndex = 0 END METHODGeSHi parsed in 0.0640699863434 seconds. The TreeNode's for the TreeList are stored with each name in the list, and accessed at several parts of the program when creating, deleting, or adjusting the the TreeList.
PROPERTY GET TreeNode(fIndex AS LONG) AS DWORD IF (fIndex >= 0) AND (fIndex <= UBOUND(FurreList)) THEN PROPERTY = FurreList(fIndex).TreeNode END IF END PROPERTY PROPERTY SET TreeNode(fIndex AS LONG, fNode AS DWORD) IF (fIndex >= 0) AND (fIndex <= UBOUND(FurreList)) THEN FurreList(fIndex).TreeNode = fNode END IF END PROPERTYGeSHi parsed in 0.0596349239349 seconds. The membership status of a Furre is checked at several places throughout the program, like with menu actions which generate the menu for the selected element.
PROPERTY GET Unknown(fIndex AS LONG) AS LONG IF (fIndex >= 0) AND (fIndex <= UBOUND(FurreList)) THEN PROPERTY = FurreList(fIndex).Unknown END IF END PROPERTYGeSHi parsed in 0.029956817627 seconds. UpdateFurre is called by the editor when changes have been made to the entry.
METHOD UpdateFurre(Furre AS FURRESTRUCTURE) AS LONG ' only update entries which have the same index ' rescan the array, verify the index, and update LOCAL fIndex, sLen AS LONG LOCAL fName,sName AS STRING fName = Furre.FurreName sName = fName & $NUL sLen = LEN(sName) ARRAY SCAN FurreList(), _ COLLATE UCASE, FROM 1 TO sLen, _ = sName, TO fIndex ' maybe add FurreList(fIndex).RecordIndex = Furre.RecordIndex ? IF fIndex > 0 THEN DECR fIndex FurreList(fIndex).FurreName = Furre.FurreName FurreList(fIndex).Notes = Furre.Notes FurreList(fIndex).SoundWhispers = Furre.SoundWhispers FurreList(fIndex).SoundSays = Furre.SoundSays FurreList(fIndex).SoundOnline = Furre.SoundOnline FurreList(fIndex).SoundOffline = Furre.SoundOffline FurreList(fIndex).GroupMember = Furre.GroupMember fIndex = fIndex OR IIF(ISTRUE FurreList(fIndex).OnlineStatus, _ %OnlineFurre, %OfflineFurre) me.Purge() END IF METHOD = fIndex END METHODGeSHi parsed in 0.12920498848 seconds. UpDateOnlineFurres is a big part of the system, this takes the string of names returned by the online server and walks through them, adjusting each entry for later updating by LoadFreshFurres. Each name is scanned in both the .FurreName and .ShortName elements of the structure and if found, (which they should be found), their status is updated. Any furre who was offline before still has a status of 0 in the .OnlineStatus and online furre's have a status of 2. If the name is found, that value is incremented resulting in a new status of 1 for fresh online furre's and 3 for those who are still online. If LoadFreshFurres finds a member with the status still at 2, that name went offline and can be set accordingly by the function. That function also resets the status to either 0 or 1 in addition to clearing the .InQuery status of each member. There's also a check in this part to set the .FreshOnline counter to enable names to debold after a couple of minutes if the system has passed the FirstFetch point, that is, successfully passed through the list once.
METHOD UpDateOnlineFurres(NamesOn AS STRING) LOCAL NameList(), Hunter AS STRING LOCAL sHunter AS STRING LOCAL NiDx, fIndex, hError AS LONG LOCAL OnlineTime AS QUAD OnlineTime = WindowTimeToUnixQuad(GetWindowTimeQuad(), UtcOffSet) ' first set everyone to being potentially offline DIM NameList(PARSECOUNT(NamesOn, $LF) - 1) PARSE NamesOn, NameList(), $LF ARRAY SORT NameList() FOR NiDx = 0 TO UBOUND(NameList) hError = 0 Hunter = NameList(NiDx) REPLACE "|" WITH " " IN Hunter ' check for html entities IF PARSECOUNT(Hunter, ANY "&;") > 1 THEN _ Hunter = HtmlToAscii(Hunter) ARRAY SCAN FurreList(), _ COLLATE UCASE, FROM 1 TO LEN(Hunter), _ = Hunter, TO fIndex IF (fIndex = 0) THEN ' check as shortname, if not found, weird error sHunter = MakeShortName(Hunter) ARRAY SCAN FurreList(), _ COLLATE UCASE, FROM 1 TO LEN(sHunter), _ = sHunter, TO fIndex hError = fIndex END IF IF (fIndex = 0) THEN ' this is a weird error ' possible to reach in theory if deleted ' after fetch is executed before results return Hunter = "Error: "& Hunter &" not found in the array" ELSE DECR fIndex ' below is the check to convert names to proper casing IF ((hError > 0) _ OR (FurreList(fIndex).FurreName <> Hunter)) THEN _ FurreList(fIndex).FurreName = Hunter INCR FurreList(fIndex).OnlineStatus ' this results in anyone going offline to remain at 2 ' new onlines is 1, and existing is 3 IF FurreList(fIndex).OnlineStatus = 1 THEN FurreList(fIndex).LastOnline = OnlineTime FurreList(fIndex).FreshOnLine _ = IIF(ISTRUE FirstFetch, %FreshCount, 0) END IF END IF NEXT NiDx END METHODGeSHi parsed in 0.250142812729 seconds. And the last property of the class, WhisperSound, which is checked by CopyDataHandler when ever it gets a %FurreWhispers message from a client. Something to tinker with is the addition of a timeout value so that when you're in a whisper conversation, the system does not whisper noise you whenever the other party whispers back, only making the sound when a preset amount of time has passed since the last whisper from that Furre.
' this should work with fIndex that would merely require ' another call to get that, so overhead is not done ' in favor of just looking it up again ' unless we mod FurreWhispers to return the fIndex ? PROPERTY GET WhisperSound(fName AS STRING, Speaks AS LONG) AS LONG LOCAL fIndex, sLen AS LONG LOCAL sName AS STRING LOCAL Furre AS FURRESTRUCTURE ' scan the list both in furrelist and shortname list sName = fName & $NUL sLen = LEN(sName) ARRAY SCAN FurreList(), _ COLLATE UCASE, FROM 1 TO sLen, _ = sName, TO fIndex IF (fIndex = 0) THEN sName = MakeShortName(fName) & $NUL ' do this JIC sLen = LEN(sName) + %ShortNameIndex - 1 ARRAY SCAN FurreList(), _ COLLATE UCASE, FROM %ShortNameIndex TO sLen, _ = sName, TO fIndex END IF IF (fIndex = 0) THEN ' return false PROPERTY = %FALSE ELSE DECR fIndex IF (ISTRUE Speaks) THEN PROPERTY = (ISTRUE FurreList(fIndex).SoundSays) ELSE PROPERTY = (ISTRUE FurreList(fIndex).SoundWhispers) END IF END IF END PROPERTYGeSHi parsed in 0.151310920715 seconds. And thus ends the trip through the FurreOnlineClass for Faux Pounce. END INTERFACE END CLASSGeSHi parsed in 0.00841689109802 seconds. |
|
| Last Updated ( Aug 20, 2010 at 01:30 PM ) |