Login Guest
May 20, 2012, 3:02 pm UTCHome arrow Coding arrow Code Chunks arrow Furre Online Class
header image
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 METHOD
GeSHi 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 IUNKNOWN
GeSHi 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.

  • Called from:
    • LoadOnlineList
    • ShowAddNewProc
        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 METHOD
GeSHi 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.

  • Called from:
    • CopyDataHandler
        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 METHOD
GeSHi 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.

  • Called from:
    • LoadFreshOnline
        PROPERTY GET CheckLarge() AS LONG
            PROPERTY = ISTRUE LargeList
        END PROPERTY
GeSHi 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.

  • Called From:
    • CopyDataHandler
    • MainWinProc
        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 METHOD
GeSHi 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.

  • Called from:
    • QueryOnlineStatus
    • LoadFreshOnline
    • LoadFreshFurres
        ' FirstCheck is used by the notification system,
        ' can we start spamming you?
        PROPERTY GET FirstCheck() AS LONG
            PROPERTY = ISTRUE FirstFetch
        END PROPERTY
GeSHi 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 PROPERTY
GeSHi 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.

  • Called from:
    • LoadGroups
        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 PROPERTY
GeSHi 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.

  • Called from:
    • MainTimerProc
        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 METHOD
GeSHi 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.

  • Called from:
    • ShowAddNewProc
    • ShowDeleteGroup
    • LoadFreshFurres
    • LoadFurreList
    • SaveOnlineList
        ' 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 PROPERTY
GeSHi parsed in 0.0647749900818 seconds.

Obviously, when I need to iterate through the list, I need the count of members in the list.

  • Called from:
    • ShowDeleteGroup
    • LoadFreshOnline
    • LoadFreshFurres
    • LoadFurreList
    • SaveOnlineList
        ' 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 PROPERTY
GeSHi 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.

  • Called from:
    • CopyDataHandler
    • ShowAddNewProc
        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 PROPERTY
GeSHi 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".

  • Called from: (just FurreName)
    • LaunchFurcadia
    • DoubleClickManager
    • RightClickManager
    • MainWinProc
        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 PROPERTY
GeSHi 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.

  • Called from:
    • CopyDataHandler
        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 METHOD
GeSHi 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.)

  • Called from:
    • LoadGroups
        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 PROPERTY
GeSHi 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.

  • Called from:
    • GroupRemove
        ' 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 METHOD
GeSHi 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.

  • Called from:
    • DoubleClickManager
        PROPERTY GET LastWhispered(fIndex AS LONG) AS STRING
            IF (fIndex >= 0) AND (fIndex <= UBOUND(FurreList)) THEN
                PROPERTY = FurreList(fIndex).lastWhispered
            END IF
        END PROPERTY
GeSHi 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.

  • Called from:
    • QueryOnlineStatus
        ' 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 PROPERTY
GeSHi 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.

  • Called from:
    • LoadGroups
        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 PROPERTY
GeSHi 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, ...

  • Called from:
    • DoubleClickManager
    • LoadFreshFurres
    • MainWinProc
    • CopyDataHandler
        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 PROPERTY
GeSHi 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.

  • Called from:
    • LoadOnline
    • GroupRemove
        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 METHOD
GeSHi 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.

  • Called from:
    • MainWinProc
        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 METHOD
GeSHi 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.

  • Called from:
    • LoadFreshFurres
        PROPERTY SET Query(fIndex AS LONG, nQuery AS LONG)
            IF (fIndex >= 0) AND (fIndex <= UBOUND(FurreList)) THEN
                FurreList(fIndex).InQuery = nQuery
            END IF
        END PROPERTY
GeSHi 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.

  • Called from:
    • ShowAddNewProc
        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 PROPERTY
GeSHi 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.

  • Called from:
    • MainWinProc
        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 METHOD
GeSHi 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.

  • Called from:
    • QueryOnlineStatus
        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 METHOD
GeSHi 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.

  • Called from:
    • ShowAddNewProc
    • LoadFurreList
    • LoadFreshFurres
    • MainWinProc
        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 PROPERTY
GeSHi 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.

  • Called from:
    • MenuSetup
    • RightClickManager
    • MainWinProc
    • CopyDataHandler
        PROPERTY GET Unknown(fIndex AS LONG) AS LONG
            IF (fIndex >= 0) AND (fIndex <= UBOUND(FurreList)) THEN
                PROPERTY = FurreList(fIndex).Unknown
            END IF
        END PROPERTY
GeSHi parsed in 0.029956817627 seconds.

UpdateFurre is called by the editor when changes have been made to the entry.

  • Called from:
    • ShowAddNewProc
        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 METHOD
GeSHi 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.

  • Called from:
    • UpdateList
        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 METHOD
GeSHi 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.

  • Called from:
    • CopyDataHandler
        ' 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 PROPERTY
GeSHi parsed in 0.151310920715 seconds.

And thus ends the trip through the FurreOnlineClass for Faux Pounce.

    END INTERFACE
END CLASS
GeSHi parsed in 0.00841689109802 seconds.
Last Updated ( Aug 20, 2010 at 01:30 PM )
header image