ea150a8931
git-svn-id: svn://10.65.10.50/trunk@33 c028cbd2-c16b-5b4b-a496-9718f37d4682
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
|