* #27232: jni: added pjproject checkout as regular git content

We will remove it once the next release of pjsip (with Android support)
comes out and is merged into SFLphone.
diff --git a/jni/pjproject-android/third_party/BaseClasses/wxlist.cpp b/jni/pjproject-android/third_party/BaseClasses/wxlist.cpp
new file mode 100644
index 0000000..df22839
--- /dev/null
+++ b/jni/pjproject-android/third_party/BaseClasses/wxlist.cpp
@@ -0,0 +1,896 @@
+//------------------------------------------------------------------------------

+// File: WXList.cpp

+//

+// Desc: DirectShow base classes - implements a non-MFC based generic list

+//       template class.

+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.

+//------------------------------------------------------------------------------

+

+#include <pjmedia-videodev/config.h>

+

+#if defined(PJMEDIA_VIDEO_DEV_HAS_DSHOW) && PJMEDIA_VIDEO_DEV_HAS_DSHOW != 0

+

+/* A generic list of pointers to objects.

+   Objectives: avoid using MFC libraries in ndm kernel mode and

+   provide a really useful list type.

+

+   The class is thread safe in that separate threads may add and

+   delete items in the list concurrently although the application

+   must ensure that constructor and destructor access is suitably

+   synchronised.

+

+   The list name must not conflict with MFC classes as an

+   application may use both

+

+   The nodes form a doubly linked, NULL terminated chain with an anchor

+   block (the list object per se) holding pointers to the first and last

+   nodes and a count of the nodes.

+   There is a node cache to reduce the allocation and freeing overhead.

+   It optionally (determined at construction time) has an Event which is

+   set whenever the list becomes non-empty and reset whenever it becomes

+   empty.

+   It optionally (determined at construction time) has a Critical Section

+   which is entered during the important part of each operation.  (About

+   all you can do outside it is some parameter checking).

+

+   The node cache is a repository of nodes that are NOT in the list to speed

+   up storage allocation.  Each list has its own cache to reduce locking and

+   serialising.  The list accesses are serialised anyway for a given list - a

+   common cache would mean that we would have to separately serialise access

+   of all lists within the cache.  Because the cache only stores nodes that are

+   not in the list, releasing the cache does not release any list nodes.  This

+   means that list nodes can be copied or rechained from one list to another

+   without danger of creating a dangling reference if the original cache goes

+   away.

+

+   Questionable design decisions:

+   1. Retaining the warts for compatibility

+   2. Keeping an element count -i.e. counting whenever we do anything

+      instead of only when we want the count.

+   3. Making the chain pointers NULL terminated.  If the list object

+      itself looks just like a node and the list is kept as a ring then

+      it reduces the number of special cases.  All inserts look the same.

+*/

+

+

+#include <streams.h>

+

+/* set cursor to the position of each element of list in turn  */

+#define INTERNALTRAVERSELIST(list, cursor)               \

+for ( cursor = (list).GetHeadPositionI()           \

+    ; cursor!=NULL                               \

+    ; cursor = (list).Next(cursor)                \

+    )

+

+

+/* set cursor to the position of each element of list in turn

+   in reverse order

+*/

+#define INTERNALREVERSETRAVERSELIST(list, cursor)        \

+for ( cursor = (list).GetTailPositionI()           \

+    ; cursor!=NULL                               \

+    ; cursor = (list).Prev(cursor)                \

+    )

+

+/* Constructor calls a separate initialisation function that

+   creates a node cache, optionally creates a lock object

+   and optionally creates a signaling object.

+

+   By default we create a locking object, a DEFAULTCACHE sized

+   cache but no event object so the list cannot be used in calls

+   to WaitForSingleObject

+*/

+CBaseList::CBaseList(__in_opt LPCTSTR pName,    // Descriptive list name

+                     INT iItems) :    // Node cache size

+#ifdef DEBUG

+    CBaseObject(pName),

+#endif

+    m_pFirst(NULL),

+    m_pLast(NULL),

+    m_Count(0),

+    m_Cache(iItems)

+{

+} // constructor

+

+CBaseList::CBaseList(__in_opt LPCTSTR pName) :  // Descriptive list name

+#ifdef DEBUG

+    CBaseObject(pName),

+#endif

+    m_pFirst(NULL),

+    m_pLast(NULL),

+    m_Count(0),

+    m_Cache(DEFAULTCACHE)

+{

+} // constructor

+

+#ifdef UNICODE

+CBaseList::CBaseList(__in_opt LPCSTR pName,    // Descriptive list name

+                     INT iItems) :    // Node cache size

+#ifdef DEBUG

+    CBaseObject(pName),

+#endif

+    m_pFirst(NULL),

+    m_pLast(NULL),

+    m_Count(0),

+    m_Cache(iItems)

+{

+} // constructor

+

+CBaseList::CBaseList(__in_opt LPCSTR pName) :  // Descriptive list name

+#ifdef DEBUG

+    CBaseObject(pName),

+#endif

+    m_pFirst(NULL),

+    m_pLast(NULL),

+    m_Count(0),

+    m_Cache(DEFAULTCACHE)

+{

+} // constructor

+

+#endif

+

+/* The destructor enumerates all the node objects in the list and

+   in the cache deleting each in turn. We do not do any processing

+   on the objects that the list holds (i.e. points to) so if they

+   represent interfaces for example the creator of the list should

+   ensure that each of them is released before deleting us

+*/

+CBaseList::~CBaseList()

+{

+    /* Delete all our list nodes */

+

+    RemoveAll();

+

+} // destructor

+

+/* Remove all the nodes from the list but don't do anything

+   with the objects that each node looks after (this is the

+   responsibility of the creator).

+   Aa a last act we reset the signalling event

+   (if available) to indicate to clients that the list

+   does not have any entries in it.

+*/

+void CBaseList::RemoveAll()

+{

+    /* Free up all the CNode objects NOTE we don't bother putting the

+       deleted nodes into the cache as this method is only really called

+       in serious times of change such as when we are being deleted at

+       which point the cache will be deleted anway */

+

+    CNode *pn = m_pFirst;

+    while (pn) {

+        CNode *op = pn;

+        pn = pn->Next();

+        delete op;

+    }

+

+    /* Reset the object count and the list pointers */

+

+    m_Count = 0;

+    m_pFirst = m_pLast = NULL;

+

+} // RemoveAll

+

+

+

+/* Return a position enumerator for the entire list.

+   A position enumerator is a pointer to a node object cast to a

+   transparent type so all we do is return the head/tail node

+   pointer in the list.

+   WARNING because the position is a pointer to a node there is

+   an implicit assumption for users a the list class that after

+   deleting an object from the list that any other position

+   enumerators that you have may be invalid (since the node

+   may be gone).

+*/

+__out_opt POSITION CBaseList::GetHeadPositionI() const

+{

+    return (POSITION) m_pFirst;

+} // GetHeadPosition

+

+

+

+__out_opt POSITION CBaseList::GetTailPositionI() const

+{

+    return (POSITION) m_pLast;

+} // GetTailPosition

+

+

+

+/* Get the number of objects in the list,

+   Get the lock before accessing the count.

+   Locking may not be entirely necessary but it has the side effect

+   of making sure that all operations are complete before we get it.

+   So for example if a list is being added to this list then that

+   will have completed in full before we continue rather than seeing

+   an intermediate albeit valid state

+*/

+int CBaseList::GetCountI() const

+{

+    return m_Count;

+} // GetCount

+

+

+

+/* Return the object at rp, update rp to the next object from

+   the list or NULL if you have moved over the last object.

+   You may still call this function once we return NULL but

+   we will continue to return a NULL position value

+*/

+__out void *CBaseList::GetNextI(__inout POSITION& rp) const

+{

+    /* have we reached the end of the list */

+

+    if (rp == NULL) {

+        return NULL;

+    }

+

+    /* Lock the object before continuing */

+

+    void *pObject;

+

+    /* Copy the original position then step on */

+

+    CNode *pn = (CNode *) rp;

+    ASSERT(pn != NULL);

+    rp = (POSITION) pn->Next();

+

+    /* Get the object at the original position from the list */

+

+    pObject = pn->GetData();

+    // ASSERT(pObject != NULL);    // NULL pointers in the list are allowed.

+    return pObject;

+} //GetNext

+

+

+

+/* Return the object at p.

+   Asking for the object at NULL ASSERTs then returns NULL

+   The object is NOT locked.  The list is not being changed

+   in any way.  If another thread is busy deleting the object

+   then locking would only result in a change from one bad

+   behaviour to another.

+*/

+__out_opt void *CBaseList::GetI(__in_opt POSITION p) const

+{

+    if (p == NULL) {

+        return NULL;

+    }

+

+    CNode * pn = (CNode *) p;

+    void *pObject = pn->GetData();

+    // ASSERT(pObject != NULL);    // NULL pointers in the list are allowed.

+    return pObject;

+} //Get

+

+__out void *CBaseList::GetValidI(__in POSITION p) const

+{

+    CNode * pn = (CNode *) p;

+    void *pObject = pn->GetData();

+    // ASSERT(pObject != NULL);    // NULL pointers in the list are allowed.

+    return pObject;

+} //Get

+

+

+/* Return the first position in the list which holds the given pointer.

+   Return NULL if it's not found.

+*/

+__out_opt POSITION CBaseList::FindI( __in void * pObj) const

+{

+    POSITION pn;

+    INTERNALTRAVERSELIST(*this, pn){

+        if (GetI(pn)==pObj) {

+            return pn;

+        }

+    }

+    return NULL;

+} // Find

+

+

+

+/* Remove the first node in the list (deletes the pointer to its object

+   from the list, does not free the object itself).

+   Return the pointer to its object or NULL if empty

+*/

+__out_opt void *CBaseList::RemoveHeadI()

+{

+    /* All we do is get the head position and ask for that to be deleted.

+       We could special case this since some of the code path checking

+       in Remove() is redundant as we know there is no previous

+       node for example but it seems to gain little over the

+       added complexity

+    */

+

+    return RemoveI((POSITION)m_pFirst);

+} // RemoveHead

+

+

+

+/* Remove the last node in the list (deletes the pointer to its object

+   from the list, does not free the object itself).

+   Return the pointer to its object or NULL if empty

+*/

+__out_opt void *CBaseList::RemoveTailI()

+{

+    /* All we do is get the tail position and ask for that to be deleted.

+       We could special case this since some of the code path checking

+       in Remove() is redundant as we know there is no previous

+       node for example but it seems to gain little over the

+       added complexity

+    */

+

+    return RemoveI((POSITION)m_pLast);

+} // RemoveTail

+

+

+

+/* Remove the pointer to the object in this position from the list.

+   Deal with all the chain pointers

+   Return a pointer to the object removed from the list.

+   The node object that is freed as a result

+   of this operation is added to the node cache where

+   it can be used again.

+   Remove(NULL) is a harmless no-op - but probably is a wart.

+*/

+__out_opt void *CBaseList::RemoveI(__in_opt POSITION pos)

+{

+    /* Lock the critical section before continuing */

+

+    // ASSERT (pos!=NULL);     // Removing NULL is to be harmless!

+    if (pos==NULL) return NULL;

+

+

+    CNode *pCurrent = (CNode *) pos;

+    ASSERT(pCurrent != NULL);

+

+    /* Update the previous node */

+

+    CNode *pNode = pCurrent->Prev();

+    if (pNode == NULL) {

+        m_pFirst = pCurrent->Next();

+    } else {

+        pNode->SetNext(pCurrent->Next());

+    }

+

+    /* Update the following node */

+

+    pNode = pCurrent->Next();

+    if (pNode == NULL) {

+        m_pLast = pCurrent->Prev();

+    } else {

+        pNode->SetPrev(pCurrent->Prev());

+    }

+

+    /* Get the object this node was looking after */

+

+    void *pObject = pCurrent->GetData();

+

+    // ASSERT(pObject != NULL);    // NULL pointers in the list are allowed.

+

+    /* Try and add the node object to the cache -

+       a NULL return code from the cache means we ran out of room.

+       The cache size is fixed by a constructor argument when the

+       list is created and defaults to DEFAULTCACHE.

+       This means that the cache will have room for this many

+       node objects. So if you have a list of media samples

+       and you know there will never be more than five active at

+       any given time of them for example then override the default

+       constructor

+    */

+

+    m_Cache.AddToCache(pCurrent);

+

+    /* If the list is empty then reset the list event */

+

+    --m_Count;

+    ASSERT(m_Count >= 0);

+    return pObject;

+} // Remove

+

+

+

+/* Add this object to the tail end of our list

+   Return the new tail position.

+*/

+

+__out_opt POSITION CBaseList::AddTailI(__in void *pObject)

+{

+    /* Lock the critical section before continuing */

+

+    CNode *pNode;

+    // ASSERT(pObject);   // NULL pointers in the list are allowed.

+

+    /* If there is a node objects in the cache then use

+       that otherwise we will have to create a new one */

+

+    pNode = (CNode *) m_Cache.RemoveFromCache();

+    if (pNode == NULL) {

+        pNode = new CNode;

+    }

+

+    /* Check we have a valid object */

+

+    if (pNode == NULL) {

+        return NULL;

+    }

+

+    /* Initialise all the CNode object

+       just in case it came from the cache

+    */

+

+    pNode->SetData(pObject);

+    pNode->SetNext(NULL);

+    pNode->SetPrev(m_pLast);

+

+    if (m_pLast == NULL) {

+        m_pFirst = pNode;

+    } else {

+        m_pLast->SetNext(pNode);

+    }

+

+    /* Set the new last node pointer and also increment the number

+       of list entries, the critical section is unlocked when we

+       exit the function

+    */

+

+    m_pLast = pNode;

+    ++m_Count;

+

+    return (POSITION) pNode;

+} // AddTail(object)

+

+

+

+/* Add this object to the head end of our list

+   Return the new head position.

+*/

+__out_opt POSITION CBaseList::AddHeadI(__in void *pObject)

+{

+    CNode *pNode;

+    // ASSERT(pObject);  // NULL pointers in the list are allowed.

+

+    /* If there is a node objects in the cache then use

+       that otherwise we will have to create a new one */

+

+    pNode = (CNode *) m_Cache.RemoveFromCache();

+    if (pNode == NULL) {

+        pNode = new CNode;

+    }

+

+    /* Check we have a valid object */

+

+    if (pNode == NULL) {

+        return NULL;

+    }

+

+    /* Initialise all the CNode object

+       just in case it came from the cache

+    */

+

+    pNode->SetData(pObject);

+

+    /* chain it in (set four pointers) */

+    pNode->SetPrev(NULL);

+    pNode->SetNext(m_pFirst);

+

+    if (m_pFirst == NULL) {

+        m_pLast = pNode;

+    } else {

+        m_pFirst->SetPrev(pNode);

+    }

+    m_pFirst = pNode;

+

+    ++m_Count;

+

+    return (POSITION) pNode;

+} // AddHead(object)

+

+

+

+/* Add all the elements in *pList to the tail of this list.

+   Return TRUE if it all worked, FALSE if it didn't.

+   If it fails some elements may have been added.

+*/

+BOOL CBaseList::AddTail(__in CBaseList *pList)

+{

+    /* lock the object before starting then enumerate

+       each entry in the source list and add them one by one to

+       our list (while still holding the object lock)

+       Lock the other list too.

+    */

+    POSITION pos = pList->GetHeadPositionI();

+

+    while (pos) {

+       if (NULL == AddTailI(pList->GetNextI(pos))) {

+           return FALSE;

+       }

+    }

+    return TRUE;

+} // AddTail(list)

+

+

+

+/* Add all the elements in *pList to the head of this list.

+   Return TRUE if it all worked, FALSE if it didn't.

+   If it fails some elements may have been added.

+*/

+BOOL CBaseList::AddHead(__in CBaseList *pList)

+{

+    /* lock the object before starting then enumerate

+       each entry in the source list and add them one by one to

+       our list (while still holding the object lock)

+       Lock the other list too.

+

+       To avoid reversing the list, traverse it backwards.

+    */

+

+    POSITION pos;

+

+    INTERNALREVERSETRAVERSELIST(*pList, pos) {

+        if (NULL== AddHeadI(pList->GetValidI(pos))){

+            return FALSE;

+        }

+    }

+    return TRUE;

+} // AddHead(list)

+

+

+

+/* Add the object after position p

+   p is still valid after the operation.

+   AddAfter(NULL,x) adds x to the start - same as AddHead

+   Return the position of the new object, NULL if it failed

+*/

+__out_opt POSITION  CBaseList::AddAfterI(__in_opt POSITION pos, __in void * pObj)

+{

+    if (pos==NULL)

+        return AddHeadI(pObj);

+

+    /* As someone else might be furkling with the list -

+       Lock the critical section before continuing

+    */

+    CNode *pAfter = (CNode *) pos;

+    ASSERT(pAfter != NULL);

+    if (pAfter==m_pLast)

+        return AddTailI(pObj);

+

+    /* set pnode to point to a new node, preferably from the cache */

+

+    CNode *pNode = (CNode *) m_Cache.RemoveFromCache();

+    if (pNode == NULL) {

+        pNode = new CNode;

+    }

+

+    /* Check we have a valid object */

+

+    if (pNode == NULL) {

+        return NULL;

+    }

+

+    /* Initialise all the CNode object

+       just in case it came from the cache

+    */

+

+    pNode->SetData(pObj);

+

+    /* It is to be added to the middle of the list - there is a before

+       and after node.  Chain it after pAfter, before pBefore.

+    */

+    CNode * pBefore = pAfter->Next();

+    ASSERT(pBefore != NULL);

+

+    /* chain it in (set four pointers) */

+    pNode->SetPrev(pAfter);

+    pNode->SetNext(pBefore);

+    pBefore->SetPrev(pNode);

+    pAfter->SetNext(pNode);

+

+    ++m_Count;

+

+    return (POSITION) pNode;

+

+} // AddAfter(object)

+

+

+

+BOOL CBaseList::AddAfter(__in_opt POSITION p, __in CBaseList *pList)

+{

+    POSITION pos;

+    INTERNALTRAVERSELIST(*pList, pos) {

+        /* p follows along the elements being added */

+        p = AddAfterI(p, pList->GetValidI(pos));

+        if (p==NULL) return FALSE;

+    }

+    return TRUE;

+} // AddAfter(list)

+

+

+

+/* Mirror images:

+   Add the element or list after position p.

+   p is still valid after the operation.

+   AddBefore(NULL,x) adds x to the end - same as AddTail

+*/

+__out_opt POSITION CBaseList::AddBeforeI(__in_opt POSITION pos, __in void * pObj)

+{

+    if (pos==NULL)

+        return AddTailI(pObj);

+

+    /* set pnode to point to a new node, preferably from the cache */

+

+    CNode *pBefore = (CNode *) pos;

+    ASSERT(pBefore != NULL);

+    if (pBefore==m_pFirst)

+        return AddHeadI(pObj);

+

+    CNode * pNode = (CNode *) m_Cache.RemoveFromCache();

+    if (pNode == NULL) {

+        pNode = new CNode;

+    }

+

+    /* Check we have a valid object */

+

+    if (pNode == NULL) {

+        return NULL;

+    }

+

+    /* Initialise all the CNode object

+       just in case it came from the cache

+    */

+

+    pNode->SetData(pObj);

+

+    /* It is to be added to the middle of the list - there is a before

+       and after node.  Chain it after pAfter, before pBefore.

+    */

+

+    CNode * pAfter = pBefore->Prev();

+    ASSERT(pAfter != NULL);

+

+    /* chain it in (set four pointers) */

+    pNode->SetPrev(pAfter);

+    pNode->SetNext(pBefore);

+    pBefore->SetPrev(pNode);

+    pAfter->SetNext(pNode);

+

+    ++m_Count;

+

+    return (POSITION) pNode;

+

+} // Addbefore(object)

+

+

+

+BOOL CBaseList::AddBefore(__in_opt POSITION p, __in CBaseList *pList)

+{

+    POSITION pos;

+    INTERNALREVERSETRAVERSELIST(*pList, pos) {

+        /* p follows along the elements being added */

+        p = AddBeforeI(p, pList->GetValidI(pos));

+        if (p==NULL) return FALSE;

+    }

+    return TRUE;

+} // AddBefore(list)

+

+

+

+/* Split *this after position p in *this

+   Retain as *this the tail portion of the original *this

+   Add the head portion to the tail end of *pList

+   Return TRUE if it all worked, FALSE if it didn't.

+

+   e.g.

+      foo->MoveToTail(foo->GetHeadPosition(), bar);

+          moves one element from the head of foo to the tail of bar

+      foo->MoveToTail(NULL, bar);

+          is a no-op

+      foo->MoveToTail(foo->GetTailPosition, bar);

+          concatenates foo onto the end of bar and empties foo.

+

+   A better, except excessively long name might be

+       MoveElementsFromHeadThroughPositionToOtherTail

+*/

+BOOL CBaseList::MoveToTail

+        (__in_opt POSITION pos, __in CBaseList *pList)

+{

+    /* Algorithm:

+       Note that the elements (including their order) in the concatenation

+       of *pList to the head of *this is invariant.

+       1. Count elements to be moved

+       2. Join *pList onto the head of this to make one long chain

+       3. Set first/Last pointers in *this and *pList

+       4. Break the chain at the new place

+       5. Adjust counts

+       6. Set/Reset any events

+    */

+

+    if (pos==NULL) return TRUE;  // no-op.  Eliminates special cases later.

+

+

+    /* Make cMove the number of nodes to move */

+    CNode * p = (CNode *)pos;

+    int cMove = 0;            // number of nodes to move

+    while(p!=NULL) {

+       p = p->Prev();

+       ++cMove;

+    }

+

+

+    /* Join the two chains together */

+    if (pList->m_pLast!=NULL)

+        pList->m_pLast->SetNext(m_pFirst);

+    if (m_pFirst!=NULL)

+        m_pFirst->SetPrev(pList->m_pLast);

+

+

+    /* set first and last pointers */

+    p = (CNode *)pos;

+

+    if (pList->m_pFirst==NULL)

+        pList->m_pFirst = m_pFirst;

+    m_pFirst = p->Next();

+    if (m_pFirst==NULL)

+        m_pLast = NULL;

+    pList->m_pLast = p;

+

+

+    /* Break the chain after p to create the new pieces */

+    if (m_pFirst!=NULL)

+        m_pFirst->SetPrev(NULL);

+    p->SetNext(NULL);

+

+

+    /* Adjust the counts */

+    m_Count -= cMove;

+    pList->m_Count += cMove;

+

+    return TRUE;

+

+} // MoveToTail

+

+

+

+/* Mirror image of MoveToTail:

+   Split *this before position p in *this.

+   Retain in *this the head portion of the original *this

+   Add the tail portion to the start (i.e. head) of *pList

+   Return TRUE if it all worked, FALSE if it didn't.

+

+   e.g.

+      foo->MoveToHead(foo->GetTailPosition(), bar);

+          moves one element from the tail of foo to the head of bar

+      foo->MoveToHead(NULL, bar);

+          is a no-op

+      foo->MoveToHead(foo->GetHeadPosition, bar);

+          concatenates foo onto the start of bar and empties foo.

+*/

+BOOL CBaseList::MoveToHead

+        (__in_opt POSITION pos, __in CBaseList *pList)

+{

+

+    /* See the comments on the algorithm in MoveToTail */

+

+    if (pos==NULL) return TRUE;  // no-op.  Eliminates special cases later.

+

+    /* Make cMove the number of nodes to move */

+    CNode * p = (CNode *)pos;

+    int cMove = 0;            // number of nodes to move

+    while(p!=NULL) {

+       p = p->Next();

+       ++cMove;

+    }

+

+

+    /* Join the two chains together */

+    if (pList->m_pFirst!=NULL)

+        pList->m_pFirst->SetPrev(m_pLast);

+    if (m_pLast!=NULL)

+        m_pLast->SetNext(pList->m_pFirst);

+

+

+    /* set first and last pointers */

+    p = (CNode *)pos;

+

+

+    if (pList->m_pLast==NULL)

+        pList->m_pLast = m_pLast;

+

+    m_pLast = p->Prev();

+    if (m_pLast==NULL)

+        m_pFirst = NULL;

+    pList->m_pFirst = p;

+

+

+    /* Break the chain after p to create the new pieces */

+    if (m_pLast!=NULL)

+        m_pLast->SetNext(NULL);

+    p->SetPrev(NULL);

+

+

+    /* Adjust the counts */

+    m_Count -= cMove;

+    pList->m_Count += cMove;

+

+    return TRUE;

+

+} // MoveToHead

+

+

+

+/* Reverse the order of the [pointers to] objects in *this

+*/

+void CBaseList::Reverse()

+{

+    /* algorithm:

+       The obvious booby trap is that you flip pointers around and lose

+       addressability to the node that you are going to process next.

+       The easy way to avoid this is do do one chain at a time.

+

+       Run along the forward chain,

+       For each node, set the reverse pointer to the one ahead of us.

+       The reverse chain is now a copy of the old forward chain, including

+       the NULL termination.

+

+       Run along the reverse chain (i.e. old forward chain again)

+       For each node set the forward pointer of the node ahead to point back

+       to the one we're standing on.

+       The first node needs special treatment,

+       it's new forward pointer is NULL.

+       Finally set the First/Last pointers

+

+    */

+    CNode * p;

+

+    // Yes we COULD use a traverse, but it would look funny!

+    p = m_pFirst;

+    while (p!=NULL) {

+        CNode * q;

+        q = p->Next();

+        p->SetNext(p->Prev());

+        p->SetPrev(q);

+        p = q;

+    }

+

+    p = m_pFirst;

+    m_pFirst = m_pLast;

+    m_pLast = p;

+

+

+#if 0     // old version

+

+    if (m_pFirst==NULL) return;          // empty list

+    if (m_pFirst->Next()==NULL) return;  // single node list

+

+

+    /* run along forward chain */

+    for ( p = m_pFirst

+        ; p!=NULL

+        ; p = p->Next()

+        ){

+        p->SetPrev(p->Next());

+    }

+

+

+    /* special case first element */

+    m_pFirst->SetNext(NULL);     // fix the old first element

+

+

+    /* run along new reverse chain i.e. old forward chain again */

+    for ( p = m_pFirst           // start at the old first element

+        ; p->Prev()!=NULL        // while there's a node still to be set

+        ; p = p->Prev()          // work in the same direction as before

+        ){

+        p->Prev()->SetNext(p);

+    }

+

+

+    /* fix forward and reverse pointers

+       - the triple XOR swap would work but all the casts look hideous */

+    p = m_pFirst;

+    m_pFirst = m_pLast;

+    m_pLast = p;

+#endif

+

+} // Reverse

+

+#endif /* PJMEDIA_VIDEO_DEV_HAS_DSHOW */