915 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			915 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
| //
 | |
| // ARCENTRY.CPP
 | |
| //
 | |
| //  Source file for ArchiveLib 1.0
 | |
| //
 | |
| //  Copyright (c) Greenleaf Software, Inc. 1994
 | |
| //  All Rights Reserved
 | |
| //
 | |
| // CONTENTS
 | |
| //
 | |
| //  ALEntry::operator new()
 | |
| //  ALEntryList::operator new()
 | |
| //  ALEntry::ALEntry()
 | |
| //  ALEntry::~ALEntry()
 | |
| //  ALEntry::Duplicate()
 | |
| //  ALEntry::InsertBefore()
 | |
| //  ALEntry::SetComment()
 | |
| //  ALEntry::CompressionRatio()
 | |
| //  ALEntryList::ALEntryList()
 | |
| //  ALEntryList::~ALEntryList()
 | |
| //  ALEntryList::SetMarkState()
 | |
| //  ALEntryList::ToggleMarks()
 | |
| //  ALEntry::GetNextEntry()
 | |
| //  ALEntryList::UnmarkDuplicates()
 | |
| //  ALEntryList::DeleteUnmarked()
 | |
| //  ALEntryList::FillListBox()          //Windows GUI only
 | |
| //  ALEntryList::SetMarksFromListBox()  //Windows GUI only
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  This file contains the source code for two class, ALEntry and
 | |
| //  ALEntryList.  They are so tightly bound together that it made
 | |
| //  sense to go ahead and stick them both in the same file.
 | |
| //
 | |
| //  Class ALEntry describes the state of an object that is in an archive.
 | |
| //  It contains a pointer to a storage object and a compression engine,
 | |
| //  which define what goes in the archive and how it is put there.  It
 | |
| //  also defines how to extract it.  The ALEntry object also contains 
 | |
| //  miscellaneous items that go with the object in the archive, such
 | |
| //  as its time/date stamp, its CRC, and its comment.
 | |
| //
 | |
| //  You have to create an ALEntry *before* you put an object into an
 | |
| //  archive.  The archiving class member function figure out what you want
 | |
| //  to do by looking at objects of the ALEntry.  You also have to read
 | |
| //  the contents of the archive into a list of ALEntry objects before you
 | |
| //  can extract anything.
 | |
| //
 | |
| //  ALEntryList is simply a class that is used to keep a linked list of
 | |
| //  ALEntry objects together.  ALEntryList objects are passed to the high
 | |
| //  level ALArchiveBase functions for common operations, such as Create()
 | |
| //  Extract(), and so on.  You get an ALEntryList back when you read the
 | |
| //  directory of an archive.  The ALEntryList object tells you everything
 | |
| //  there is to know about the object stored in the archive.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //  May 23, 1994  1.0A  : First release
 | |
| //
 | |
| //
 | |
| 
 | |
| #include "arclib.h"
 | |
| #include <utility.h>
 | |
| #pragma hdrstop
 | |
| 
 | |
| //
 | |
| // void * ALEntry::operator new( size_t size )
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  size  :  The number of bytes needed to create a new ALEntry object.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  A pointer to the newly allocated storage area, or 0 if no storage
 | |
| //  was available.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  When using a DLL, it is easy to create a dangerous situation when 
 | |
| //  creating objects whose ctor and dtor are both in the DLL.  The problem
 | |
| //  arises because when you create an object using new, the memory for
 | |
| //  the object will be allocated from the EXE.  However, when you destroy
 | |
| //  the object using delete, the memory is freed inside the DLL.  Since
 | |
| //  the DLL doesn't really own that memory, bad things can happen.
 | |
| //
 | |
| //  But, you say, won't the space just go back to the Windows heap regardless
 | |
| //  of who tries to free it?  Maybe, but maybe not.  If the DLL is using 
 | |
| //  a subsegment allocation schemed, it might do some sort of local free
 | |
| //  before returning the space to the windows heap.  That is the point where
 | |
| //  you could conceivably cook your heap.
 | |
| //
 | |
| //  By providing our own version of operator new inside this class, we
 | |
| //  ensure that all memory allocation for the class will be done from
 | |
| //  inside the DLL, not the EXE calling the DLL.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| #if defined( AL_BUILDING_DLL )
 | |
| void AL_DLL_FAR * AL_PROTO ALEntry::operator new( size_t size )
 | |
| {
 | |
|   return ::new char[ size ];
 | |
| }
 | |
| #endif
 | |
| 
 | |
| //
 | |
| // void * ALEntryList::operator new( size_t size )
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  size  :  The number of bytes that are going to need to be allocated
 | |
| //           to create a new ALEntryList object.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  A pointer to the newly allocated storage area.  A 0 is returned if no
 | |
| //  storage could be found.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  Look at the explanation for ALEntry::operator new(), directly above
 | |
| //  this guy.  The description is identical.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| #if defined( AL_BUILDING_DLL )
 | |
| void AL_DLL_FAR * AL_PROTO ALEntryList::operator new( size_t size )
 | |
| {
 | |
|   return ::new char[ size ];
 | |
| }
 | |
| #endif
 | |
| 
 | |
| //
 | |
| // ALEntry::ALEntry( ALEntryList &list,
 | |
| //                   ALStorage *object,
 | |
| //                   ALCompressionEngine *engine )
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  list        :  A reference to the list the ALEntry object is going to
 | |
| //                 be linked into. ALEntry objects aren't allowed to exist
 | |
| //                 without being in a list.
 | |
| //
 | |
| //  object      :  A pointer to the storage object that is attached to this
 | |
| //                 entry.  Remember, this is an unopened storage object,
 | |
| //                 so it is not consuming very much space.  It is okay
 | |
| //                 to have a zillion or so of these just lying around.
 | |
| //                 Don't forget that the ALEntry dtor is going to destroy
 | |
| //                 this guy for you, don't you dare try it!.
 | |
| //
 | |
| //  engine      :  A pointer to the compression engine that is going to
 | |
| //                 be used to create/insert/extract the storage object
 | |
| //                 to/from the archive.  Just like with the compression
 | |
| //                 engine, it is a low cost object, and you can keep lots
 | |
| //                 of them on hand.  This engine will be destroyed in the
 | |
| //                 ALEntry dtor, so be sure to give up any claim you might
 | |
| //                 have on this guy.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  Nothing, this is a ctor.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  This ctor creates a new ALEntry object.  You can do this by hand, but
 | |
| //  frequently you will ask ArchiveLib to create ALEntry objects for you,
 | |
| //  maybe by pulling them out of a list box, or reading them in from and
 | |
| //  archive.  Note that ALEntry objects aren't allowed to ever exist
 | |
| //  outside a list, each entry absolutely has to appear in a list.  
 | |
| //
 | |
| //  dtor issues relating to the ALEntry object are very important.  Since
 | |
| //  ALEntry objects always are part of a list, it made sense for the 
 | |
| //  ALEntryList destructor to clean up all the entries in its list.  So
 | |
| //  even though you might have created this ALEntry object, you don't get to
 | |
| //  delete it, that will be done for you.
 | |
| //
 | |
| //  Also, the storage object and compression engine in the ALEntry object
 | |
| //  are going to be automatically destroyed by the ALEntry dtor.  Don't
 | |
| //  even think about trying it yourself!
 | |
| //
 | |
| //  You can think of an ALEntryList as a directory of an archive, and each
 | |
| //  ALEntry object in the list is a single entry in that directory.  
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| AL_PROTO ALEntry::ALEntry( ALEntryList &list,
 | |
|                           ALStorage *object,
 | |
|                           ALCompressionEngine *engine )
 | |
| : mrList( list ) // Initialize our own pointer to the list we will
 | |
|   // be a member of.
 | |
| {
 | |
|   mpNextItem = this;
 | |
|   mpPreviousItem = this;
 | |
|   mpStorageObject = object;
 | |
|   mpCompressionEngine = engine;
 | |
|   mlCompressedSize = -1;
 | |
|   mlCompressedObjectPosition = -1;
 | |
|   miMark = 1; //Always construct with the mark turned on
 | |
|   mszComment = 0;
 | |
|   //
 | |
|   // I check for the object member to be non-zero because of a clunky design
 | |
|   // choice I made a while back.  Each ALEntryList has an ALEntry member that
 | |
|   // points to the first and last members of the list.  I could have (and
 | |
|   // probably should have) made the root of the list just be a pair of pointers,
 | |
|   // instead of a dummy ALEntry.  Anyway, I can tell that dummy entry apart
 | |
|   // from the valid entries by virtue of the fact that it has a null
 | |
|   // pointer in its object pointer.
 | |
|   //
 | |
|   // So anyway, when I create this dummy object, I don't want to try to add
 | |
|   // it to the list, because by definition it is already in the list.  So
 | |
|   // I do a check before adding any ALEntry to the list.
 | |
|   //
 | |
|   if ( object )
 | |
|     InsertBefore( *list.mpListHead );
 | |
| }
 | |
| 
 | |
| //
 | |
| // ALEntry::~ALEntry()
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  Nothing.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  Nothing, this is a destructor.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  This destructor should normally be called by the ALEntryList dtor.  The
 | |
| //  list that owns an entry will always try to delete it when the list
 | |
| //  is deleted.
 | |
| //
 | |
| //  The ALEntry object tries to delete three dynamically allocated objects
 | |
| //  that it has control over:  the storage object, the compression engine,
 | |
| //  and the comment.  In each case it won't do it if the object pointer
 | |
| //  is 0.  This provides a convenient mechanism for you to steal a storage
 | |
| //  object from an ALEntry.  All you have to do is take the pointer, and
 | |
| //  then sent ALEntry::mpStorageObject to 0.  This is an especially useful
 | |
| //  thing to do for ALMemory objects.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| AL_PROTO ALEntry::~ALEntry()
 | |
| {
 | |
|   AL_ASSERT( GoodTag(), "~ALEntry: Attempting to delete invalid object" );
 | |
|   if ( mszComment )
 | |
|     delete[] mszComment;
 | |
|   if ( mpStorageObject != 0 )
 | |
|     delete mpStorageObject;
 | |
|   if ( mpCompressionEngine != 0 )
 | |
|     delete mpCompressionEngine;
 | |
|   AL_ASSERT( mpNextItem != 0 ,"~ALEntry: next item is null" );
 | |
|   AL_ASSERT( mpPreviousItem != 0, "~ALEntry: previous item is null" );
 | |
| 
 | |
|   ALEntry *next_job = mpNextItem;
 | |
|   ALEntry *previous_job = mpPreviousItem;
 | |
| 
 | |
|   if ( next_job != this ) {
 | |
|     next_job->mpPreviousItem = previous_job;
 | |
|     previous_job->mpNextItem = next_job;
 | |
|   }
 | |
|   //
 | |
|   // Note that I check the object twice, one at the start of the dtor, and
 | |
|   // once again at the end.  With all the linked list and dynamic deletion
 | |
|   // being done here, it seems like it would be really easy to hose things
 | |
|   // up if any mistakes were made.
 | |
|   //
 | |
|   AL_ASSERT( GoodTag(), "~ALEntry: Attempting to delete invalid object" );
 | |
| }
 | |
| 
 | |
| //
 | |
| // int ALEntry::Duplicate( ALEntryList &list )
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  list  : A list of ALEntry objects to scan.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  0 if the entry is not duplicated, 1 if it is.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  This function is used to scan a list of ALEntry objects to see if
 | |
| //  any of them have the same name as this.  Unmarked objects are ignored.
 | |
| //  All the function does is zip through the ALEntryList, checking each
 | |
| //  marked member for an ASCII match with the name of the storage object
 | |
| //  pointed to by this.  You can see that the case sensitivity of this
 | |
| //  is observed when making the comparison.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| int AL_PROTO ALEntry::Duplicate( ALEntryList &list )
 | |
| {
 | |
|   char *name = mpStorageObject->mName;
 | |
|   int case_sensitive = mpStorageObject->mName.mCase == AL_MIXED;
 | |
|   ALEntry *job = list.GetFirstEntry();
 | |
|   while ( job ) {
 | |
|     if ( job->GetMark() && job != this ) {
 | |
|       if ( case_sensitive ) {
 | |
|         if ( strcmp( name, job->mpStorageObject->mName ) == 0 )
 | |
|           return 1;
 | |
|       } else {
 | |
|         if ( stricmp( name, job->mpStorageObject->mName ) == 0 )
 | |
|           return 1;
 | |
|       }
 | |
|     }
 | |
|     job = job->GetNextEntry();
 | |
|   }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| //
 | |
| // PROTECTED FUNCTION
 | |
| //
 | |
| // void ALEntry::InsertBefore( ALEntry &job )
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  job : A reference to another job in the target list.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  Nothing.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  This function is used inside the ALEntryList class to add a new ALEntry
 | |
| //  object to an ALEntryList.  Since the list is a doubly linked list, the
 | |
| //  code to do the job is pretty simple.  It would have been a little more
 | |
| //  complicated if I used a pair of pointers in the ALEntryList to start
 | |
| //  the list, instead of a dummy ALEntry object.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| void AL_PROTO ALEntry::InsertBefore( ALEntry &job )
 | |
| {
 | |
|   mpNextItem = &job;
 | |
|   mpPreviousItem = job.mpPreviousItem;
 | |
|   (job.mpPreviousItem)->mpNextItem = this;
 | |
|   job.mpPreviousItem = this;
 | |
| }
 | |
| 
 | |
| //
 | |
| // int ALEntry::SetComment( const char *comment )
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  comment : The new comment that is going to be associated with the
 | |
| //            ALEntry object.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  AL_SUCCESS if the new comment was set, < 0 if an error occurred.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  Before adding an object to an archive, you may want to change or set
 | |
| //  its comment.  You do so by calling this function before performing any
 | |
| //  operation that will write the directory, such as Create() or 
 | |
| //  WriteDirectory().  It has to dynamically allocate the space in the
 | |
| //  ALEntry object in order to store the new comment.  This is good for
 | |
| //  you, because it means you don't have to worry about who owns the comment
 | |
| //  you just passed in.  
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| int AL_PROTO ALEntry::SetComment( const char AL_DLL_FAR *comment )
 | |
| {
 | |
|   if ( mszComment )
 | |
|     delete[] mszComment;
 | |
|   if ( comment ) {
 | |
|     mszComment = new char[ strlen( comment ) + 1 ];
 | |
|     if ( mszComment )
 | |
|       strcpy( mszComment, comment );
 | |
|     else
 | |
|       return mrList.mStatus.SetError( AL_CANT_ALLOCATE_MEMORY,
 | |
|                                      "Failed to allocate memory when "
 | |
|                                      "adding comment to storage object %s",
 | |
|                                      (char *) mpStorageObject->mName );
 | |
|   } else
 | |
|     mszComment = 0;
 | |
|   return AL_SUCCESS;
 | |
| }
 | |
| 
 | |
| //
 | |
| // int ALEntry::CompressionRatio()
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  None.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  The integer representing the compression ratio.  The ration is a number
 | |
| //  from 0 to 100 (or maybe more) with 0 being perfect compression.
 | |
| //
 | |
| //  It is possible to get a -1 back from this routine if the compression
 | |
| //  ratio is not presently known.  This will be the case if you have
 | |
| //  not created the archive yet, or have a new object that hasn't been
 | |
| //  inserted yet.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  This calculates and returns the compression ratio.  We don't store the
 | |
| //  ratio in ALEntry, because it is so darned easy to calculate when
 | |
| //  we need it.  However, there are going to be times when we don't have
 | |
| //  it.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| int AL_PROTO ALEntry::CompressionRatio()
 | |
| {
 | |
|   long uncompressed_size = mpStorageObject->GetSize();
 | |
| 
 | |
|   if ( uncompressed_size  <= 0 )
 | |
|     return -1;
 | |
|   if ( mlCompressedSize <= 0 )
 | |
|     return -1;
 | |
|   return (int) ( 100 * mlCompressedSize / uncompressed_size );
 | |
| }
 | |
| 
 | |
| 
 | |
| //
 | |
| // ALEntryList::ALEntryList( ALMonitor * monitor = 0 )
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  monitor : A pointer to a monitor that will be used whenever we are
 | |
| //            processing objects in the list.  If no argument is supplied,
 | |
| //            the default argument value of 0 is used.  When the ctor sees
 | |
| //            that the value of the monitor pointer is 0, it assigns the
 | |
| //            default monitor instead.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  No returns from constructors.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  Constructing an ALEntryList object doesn't take much work.  I have to
 | |
| //  initialize two data members.  The first is the pointer to the monitor
 | |
| //  that will be used when processing objects in the list.  The second is
 | |
| //  the root of the linked list, which is a dummy ALEntry object.  Note
 | |
| //  that the root is created as a dummy by setting the storage object pointer
 | |
| //  to 0.
 | |
| //
 | |
| //  The default monitor is defined below.  If you don't specify a real
 | |
| //  monitor, you get the default, which is a do nothing function.  Everyone
 | |
| //  can share one instance of the default monitor, because it doesn't have
 | |
| //  any data members to be concerned about.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| ALMonitor ALDefaultMonitor( AL_MONITOR_OBJECTS );
 | |
| 
 | |
| AL_PROTO ALEntryList::ALEntryList( ALMonitor AL_DLL_FAR * monitor /* = 0 */ )
 | |
| : mrMonitor( monitor ? *monitor : ALDefaultMonitor )
 | |
| {
 | |
|   mpListHead = new ALEntry( *this, 0, 0 );
 | |
| }
 | |
| 
 | |
| //
 | |
| // ALEntryList::~ALEntryList()
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  None.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  None, destructors don't get to return anything.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  The destructor for ALEntryList goes through the list and deletes every
 | |
| //  ALEntry object it finds.  Note that this also causes the ALEntry
 | |
| //  object to destroy its storage object and compression engine.  Once
 | |
| //  the whole list is obliterated, the list head ALEntry object can be
 | |
| //  safely deleted.  Then the whole thing is done.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| AL_PROTO ALEntryList::~ALEntryList()
 | |
| {
 | |
|   AL_ASSERT( GoodTag(), "~ALEntryList: attempting to delete invalid object" );
 | |
|   ALEntry *job = GetFirstEntry();
 | |
|   while ( job ) {
 | |
|     ALEntry *next_job = job->GetNextEntry();
 | |
|     delete job;
 | |
|     job = next_job;
 | |
|   }
 | |
|   if ( mpListHead )
 | |
|     delete mpListHead;
 | |
|   AL_ASSERT( GoodTag(), "~ALEntryList: attempting to delete invalid object" );
 | |
| }
 | |
| 
 | |
| // PROTECTED FUNCTION
 | |
| //
 | |
| //  int ALEntryList::SetMarkState( const char *name,
 | |
| //                                 short int new_state )
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  name       :  The object name, specifying which storage objects are
 | |
| //                to have their state set.  This name can include
 | |
| //                wild card characters.  Note that passing a null
 | |
| //                pointer here will cause a match to *every* object name.
 | |
| //
 | |
| //  new_state  :  The new state that the ALEntry mark should be set to.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  A count of the number of ALEntry objects whose state was changed.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  This protected function is used internally to help out a couple of the 
 | |
| //  public functions.  It rips through every entry of the list, checks to
 | |
| //  see if storage object associate with the entry has a name that matches
 | |
| //  the wildcard specification, and sets the mark if it does.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| int AL_PROTO ALEntryList::SetMarkState( const char AL_DLL_FAR *name,
 | |
|                                        short int new_state )
 | |
| {
 | |
|   int count = 0;
 | |
| 
 | |
|   ALEntry *job = GetFirstEntry();
 | |
|   while ( job ) {
 | |
|     if ( name ) {
 | |
|       if ( job->mpStorageObject->mName.WildCardMatch( name ) ) {
 | |
|         job->SetMarkState( new_state );
 | |
|         count++;
 | |
|       }
 | |
|     } else {
 | |
|       job->SetMarkState( new_state );
 | |
|       count++;
 | |
|     }
 | |
|     job = job->GetNextEntry();
 | |
|   }
 | |
|   return count;
 | |
| }
 | |
| 
 | |
| //
 | |
| // int ALEntryList::ToggleMarks()
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  None.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  A count of the number of entries whose mark was changed.
 | |
| //  (Just the total number of entries.)
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  This simple member function just goes through the entire list,
 | |
| //  toggling the mark state of every entry.  In other words, if the mark
 | |
| //  was previously set, it will now be cleared, and vice versa.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| int AL_PROTO ALEntryList::ToggleMarks()
 | |
| {
 | |
|   int count = 0;
 | |
|   ALEntry *job = GetFirstEntry();
 | |
|   while ( job ) {
 | |
|     job->SetMarkState( (short int) !job->GetMark() );
 | |
|     job = job->GetNextEntry();
 | |
|     count++;
 | |
|   }
 | |
|   return count;
 | |
| }
 | |
| 
 | |
| //
 | |
| // ALEntry * ALEntry::GetNextEntry()
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  None.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  A pointer to the next entry in the list.  If the next entry is the
 | |
| //  list head, it means we have reached the end of the list, and a value
 | |
| //  of 0 is returned.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  This function is used to iterate through the list.  Each entry has 
 | |
| //  a pointer to the next and previous entries, so this function is really
 | |
| //  simple.  The only complications comes from trying to detect the end of
 | |
| //  the list, which is denoted by the list head instance of ALEntry.  We
 | |
| //  can tell it apart from all the legitimate entries by the fact that
 | |
| //  its storage object is 0.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| ALEntry AL_DLL_FAR * AL_PROTO ALEntry::GetNextEntry()
 | |
| {
 | |
|   ALEntry *next_entry = this->mpNextItem;
 | |
|   //
 | |
|   // The list head has the special case where both the compression engine
 | |
|   // and storage object pointers are 0, and that makes the end of the list.
 | |
|   //
 | |
|   if ( mpNextItem->mpStorageObject == 0 )
 | |
|     return 0;
 | |
|   else
 | |
|     return next_entry;
 | |
| }
 | |
| 
 | |
| //
 | |
| // ALEntry * ALEntryList::GetFirstEntry()
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  None.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  A pointer to the first valid ALEntry object in the list, or 0 if there
 | |
| //  are no entries.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  If you are going to iterate through the entire list, this function is
 | |
| //  used to start you off.  It gets the first entry in the list by call
 | |
| //  GetNextEntry() for the list head.  Don't worry about what happens if
 | |
| //  the list is empty, the GetNextEntry() code figures that out with no
 | |
| //  problem, and returns a 0.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| ALEntry AL_DLL_FAR * AL_PROTO ALEntryList::GetFirstEntry()
 | |
| {
 | |
|   return mpListHead->GetNextEntry();
 | |
| }
 | |
| 
 | |
| //
 | |
| // void ALEntryList::UnmarkDuplicates( ALEntryList &list,
 | |
| //                                     const char *error_message = 0 )
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  list           :  The list that is going to be compared to this.
 | |
| //
 | |
| //  error_message  : Each entry in this that turns out to have a duplicate
 | |
| //                   entry in the list argument will not only be unmarked,
 | |
| //                   it will also have its error status set, if an error
 | |
| //                   message is provide.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  Nothing.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  I think this function is a little confusing.  At first blush, you would
 | |
| //  probably expect this function to scan all the items in a single list,
 | |
| //  and unmark any object that turn out to have duplicates elsewhere 
 | |
| //  in the list.  Unfortunately, it doesn't work that way.
 | |
| //
 | |
| //  Instead, this function goes through the list specified by this, and
 | |
| //  checks to see if each entry in this appears in the list specified by
 | |
| //  the list parameter.  This means that we are working with two different
 | |
| //  lists, which certainly offers plenty of chances to get confused.
 | |
| //
 | |
| //  Anyway, each entry in this that turns out to have a duplicate gets its
 | |
| //  mark cleared.  If the calling program specifies and error message,
 | |
| //  the entry also gets its mStatus error member set to flag this as an
 | |
| //  error.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| void AL_PROTO ALEntryList::UnmarkDuplicates( ALEntryList &list,
 | |
|                                             const char *error_message /* = 0 */ )
 | |
| {
 | |
|   ALEntry *job = GetFirstEntry();
 | |
|   while ( job ) {
 | |
|     if ( job->GetMark() ) {
 | |
|       if ( job->Duplicate( list ) ) {
 | |
|         job->ClearMark();
 | |
|         if ( error_message && error_message[ 0 ] != '\0' )
 | |
|           job->mpStorageObject->mStatus.SetError(
 | |
|             AL_DUPLICATE_ENTRY,
 | |
|             error_message );
 | |
|       }
 | |
|     }
 | |
|     job = job->GetNextEntry();
 | |
|   }
 | |
| }
 | |
| 
 | |
| //
 | |
| // int ALEntryList::DeleteUnmarked()
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  None.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  The number of entries that are deleted.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  Sometimes you may have a list with a whole bunch of unmarked entries.
 | |
| //  Those unmarked entries are just sitting there taking up space, so it
 | |
| //  would be handle to be able to just delete them.  That is what this
 | |
| //  function does.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| int AL_PROTO ALEntryList::DeleteUnmarked()
 | |
| {
 | |
|   ALEntry *job;
 | |
|   int count = 0;
 | |
| 
 | |
|   job = GetFirstEntry();
 | |
|   while ( job ) {
 | |
|     ALEntry *next_job = job->GetNextEntry();
 | |
|     if ( job->GetMark() == 0 ) {
 | |
|       count++;
 | |
|       delete job;
 | |
|     }
 | |
|     job = next_job;
 | |
|   }
 | |
|   return count;
 | |
| }
 | |
| 
 | |
| //
 | |
| // in ALEntryList::FillListBox( HWND hDlg, int list_box = -1 )
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  hDlg       : The handle of the dialog box that contains the list box
 | |
| //               control.  If the list box is not a control in a dialog,
 | |
| //               set the next parameter to -1, and just pass the handle
 | |
| //               of the list box in this argument.
 | |
| //
 | |
| //  list_box   : The ID of the list box, if and only if the list box is
 | |
| //               in a dialog box specified by by the hDlg argument.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  The number of entries that were stuffed into the list box.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  This function is a handy helper when using the Windows GUI.  It goes
 | |
| //  through an ALEntryList, and finds all the marked entries.  For every
 | |
| //  marked entry, it stuffs the name of the storage object into the list box.
 | |
| //  This means that if you are planning on letting the user select a list
 | |
| //  of storage objects, you can initialize the list with just one 
 | |
| //  function call.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| #if defined( AL_WINDOWS_GUI )
 | |
| int AL_PROTO ALEntryList::FillListBox( HWND hDlg, int list_box /* = -1 */ )
 | |
| {
 | |
|   HWND window;
 | |
| 
 | |
|   if ( list_box != -1 )
 | |
|     window = GetDlgItem( hDlg, (short int) list_box );
 | |
|   else
 | |
|     window = hDlg;
 | |
|   SendMessage( window, LB_RESETCONTENT, 0, 0 );
 | |
|   ALEntry *job = GetFirstEntry();
 | |
|   int count = 0;
 | |
|   while ( job ) {
 | |
|     if ( job->GetMark() ) {
 | |
|       count++;
 | |
|       SendMessage( window,
 | |
|                   LB_ADDSTRING,
 | |
|                   0,
 | |
|                   (LPARAM)( (LPSTR) job->mpStorageObject->mName ) );
 | |
|     }
 | |
|     job = job->GetNextEntry();
 | |
|   }
 | |
|   if ( count == 0 )
 | |
|     SendMessage( window,
 | |
|                 LB_ADDSTRING,
 | |
|                 0,
 | |
|                 (LPARAM)( (LPSTR) "<none>" ) );
 | |
|   return count;
 | |
| }
 | |
| 
 | |
| 
 | |
| //
 | |
| // int ALEntryList::SetMarksFromListBox( HWND hDlg, int list_box = - 1 )
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  hDlg        : The handle of the dialog box that contains the list box.
 | |
| //                If the list box control is standalone window, this parameter
 | |
| //                can be its handle, if the list_box argument is set to -1.
 | |
| //
 | |
| //  list_box    : The ID of the list box, if and only if it is contained in
 | |
| //                a dialog box whose handle is specified in the hDlg param.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  A count of the number of items whose marks were set.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  This function is called after you have given a user the opportunity
 | |
| //  to set and clear items in a multiselection list box.  Once the user
 | |
| //  has done so, you can call this function, which will go through the
 | |
| //  list and set all the marks that have been set in the list box by the
 | |
| //  user.  Note that it will not clear the marks on any of the ALEntry
 | |
| //  objects in the list, you might want to do that first.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| int AL_PROTO ALEntryList::SetMarksFromListBox( HWND hDlg, int list_box /* = -1 */ )
 | |
| {
 | |
|   HWND window;
 | |
| 
 | |
|   if ( list_box != -1 )
 | |
|     window = GetDlgItem( hDlg, (short int) list_box );
 | |
|   else
 | |
|     window = hDlg;
 | |
| 
 | |
|   WORD count = (WORD) SendMessage( window, LB_GETSELCOUNT, 0, 0L );
 | |
|   int *items = new int[ count ];
 | |
|   if ( items == 0 )
 | |
|     return mStatus.SetError( AL_CANT_ALLOCATE_MEMORY,
 | |
|                             "Memory allocation failure in SetMarksFromListBox()" );
 | |
| #ifdef AL_FLAT_MODEL
 | |
|   if ( count != (WORD) SendMessage( window, LB_GETSELITEMS, count, (LPARAM) ( items ) ) ) {
 | |
| #else
 | |
|     if ( count != (WORD) SendMessage( window, LB_GETSELITEMS, count, (LPARAM) ( (int _far * ) items ) ) ) {
 | |
| #endif
 | |
|       mStatus.SetError( AL_LOGIC_ERROR,
 | |
|                        "Logic error in SetMarksFromListBox()."
 | |
|                        "Mismatch in select count from list box." );
 | |
|       delete[] items;
 | |
|       return AL_LOGIC_ERROR;
 | |
|     }
 | |
|     for ( WORD i = 0 ; i < count ; i++ ) {
 | |
|       WORD length = (WORD) SendMessage( window, LB_GETTEXTLEN, (short int) items[ i ], 0L );
 | |
|       AL_ASSERT( length != (WORD) LB_ERR, "SetMarksFromListBox: LB_ERR returned from list box" );
 | |
|       if ( length > 0 ) {
 | |
|         char *name = new char[ length + 1 ];
 | |
|         if ( name ) {
 | |
|           if ( SendMessage( window, LB_GETTEXT, (short int) items[ i ], (LPARAM)( (LPSTR) name ) ) >= 0 )
 | |
|             SetMarks( name );
 | |
|           delete[] name;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     delete[] items;
 | |
|     return count;
 | |
|   }
 | |
| 
 | |
| #endif
 |