Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
CatSearchThread.c
/* |
File: CatSearchThread.c |
Contains: Contains routines responsible for searching for files via PBCatalogSearchAsync() |
within a cooperative thread as well as opening and maintaining a simple search |
window. We call the Async version of PBCatalogSearch for better responsiveness. |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. |
("Apple") in consideration of your agreement to the following terms, and your |
use, installation, modification or redistribution of this Apple software |
constitutes acceptance of these terms. If you do not agree with these terms, |
please do not use, install, modify or redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and subject |
to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs |
copyrights in this original Apple software (the "Apple Software"), to use, |
reproduce, modify and redistribute the Apple Software, with or without |
modifications, in source and/or binary forms; provided that if you redistribute |
the Apple Software in its entirety and without modifications, you must retain |
this notice and the following text and disclaimers in all such redistributions of |
the Apple Software. Neither the name, trademarks, service marks or logos of |
Apple Computer, Inc. may be used to endorse or promote products derived from the |
Apple Software without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or implied, |
are granted by Apple herein, including but not limited to any patent rights that |
may be infringed by your derivative works or by other works in which the Apple |
Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO |
WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED |
WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN |
COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR |
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION |
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT |
(INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN |
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
Copyright © 2001 Apple Computer, Inc., All Rights Reserved |
*/ |
#ifdef __APPLE_CC__ |
#include <Carbon/Carbon.h> |
#else |
#include <Carbon.h> |
#endif |
#include <string.h> |
#include "Main.h" |
extern GlobalsStruct g; |
// State information passed into each thread |
struct ThreadWindowInfoStruct |
{ |
OSType windowidentifier; |
FSCatalogBulkParam searchPB; |
EventLoopTimerRef threadIdleTimerRef; |
Boolean threadDone; |
CFStringRef nameToMatch; |
UInt32 numMatches; |
Boolean threadDoneProcessing; |
WindowRef window; |
}; |
typedef struct ThreadWindowInfoStruct ThreadWindowInfoStruct; |
pascal void CatalogSearchCompletionRoutine( ParmBlkPtr paramBlock ); |
static pascal OSStatus MyThreadWindowEventHandlerProc( EventHandlerCallRef inCallRef, EventRef inEvent, void* inUserData ); |
static pascal void IdleTask( EventLoopTimerRef inTimer, void *inUserData ); |
// This cooperative thread loops searches the volume which the application resides on and finds all files whose name contains |
// the string specified within the search tab pane. |
pascal void ProgressThread( void *refCon ) |
{ |
#define kMaximumObjects 20 // PBCatSearch returns when it finds 10 matches |
FSSpec spec; |
FSRef rootFSRef; |
FSIterator iterator; |
FSCatalogInfo searchInfo1; |
FSCatalogInfo searchInfo2; |
FSCatalogInfo catalogInfos[kMaximumObjects]; |
FSRef refs[kMaximumObjects]; |
FSSpec specs[kMaximumObjects]; |
HFSUniStr255 names[kMaximumObjects]; |
FSSearchParams searchCriteria; |
ControlRef control; |
ProcessSerialNumber psn; |
ProcessInfoRec processInfo; |
char buffer[256]; |
Str255 s; |
SInt32 displayedNumberOfMatches = 0; |
OSErr err = noErr; |
ControlID controlID = { 'Extn', 1 }; // Static text control of what we are searching for |
ThreadWindowInfoStruct *windowInfo = (ThreadWindowInfoStruct*) NewPtrClear( sizeof(ThreadWindowInfoStruct) ); |
static const EventTypeSpec windowEvents[] = |
{ |
{ kEventClassCommand, kEventCommandProcess }, |
{ kEventClassWindow, kEventWindowClose } |
}; |
windowInfo->windowidentifier = kThreadWindowType; |
windowInfo->nameToMatch = refCon; // Pascal string to match against |
windowInfo->numMatches = 0; |
windowInfo->threadDoneProcessing = false; |
windowInfo->threadDone = false; |
err = CreateWindowFromNib( g.mainNibRef, CFSTR("ProgressThreadWindow"), &(windowInfo->window) ); |
if ( err != noErr ) goto Bail; |
SetWRefCon( windowInfo->window, (long) windowInfo ); |
GetControlByID( windowInfo->window, &controlID, &control ); |
err = SetControlData( control, 0, kControlStaticTextCFStringTag, sizeof(CFStringRef), &(windowInfo->nameToMatch) ); |
if ( err != noErr ) |
{ |
CFStringGetPascalString( windowInfo->nameToMatch, (StringPtr)buffer, 256, CFStringGetSystemEncoding() ); |
err = SetControlData( control, 0, kControlStaticTextTextTag, buffer[0], buffer+1 ); |
} |
// Get information about the location of this application, so we can pass the vRefNum into PBCatSearch() |
processInfo.processAppSpec = &spec; |
processInfo.processName = nil; |
processInfo.processInfoLength = sizeof( ProcessInfoRec ); |
(void) GetCurrentProcess( &psn ); |
(void) GetProcessInformation( &psn, &processInfo ); |
/* |
* Get FSRef of container to search. The container must be the root directory of a volume |
* unless the volume supports subtree iterators. You can check the bSupportsSubtreeIterators |
* vMAttrib bit returned by PBHGetVolParms to see if a volume supports subtree iterators |
* (most volumes don't support subtree iterators). |
*/ |
err = FSGetVolumeInfo( spec.vRefNum, 0, NULL, kFSVolInfoNone, NULL, NULL, &rootFSRef ); |
if ( err != noErr ) goto Bail; |
/* |
* Open an iterator for that container. This must use the iterator flag kFSIterateSubtree |
* because CatalogSearch only knows how to search a subtree. |
*/ |
err = FSOpenIterator( &rootFSRef, kFSIterateSubtree, &iterator ); |
if ( err != noErr ) goto Bail; |
memset( &(windowInfo->searchPB), 0, sizeof(windowInfo->searchPB) ); // Initialize the param block |
CFStringGetCString( windowInfo->nameToMatch, buffer, 256, kCFStringEncodingUnicode ); // Get the unicode string |
/* |
* Initialize the searchCriteria, the searchInfo1 and the searchInfo2 records. |
* These tell FSCatalogSearch what to find. |
*/ |
searchCriteria.searchTime = 0; /* timeout as used by a Time Manager task, or 0 for no timeout */ |
searchCriteria.searchBits = fsSBPartialName + fsSBFlAttrib; /* fields to look at in searchInfo1 and searchInfo2 structs */ |
searchCriteria.searchName = (UniChar*) buffer; |
searchCriteria.searchNameLength = CFStringGetLength(windowInfo->nameToMatch); |
searchCriteria.searchInfo1 = &searchInfo1; /* the catalog info to match */ |
searchCriteria.searchInfo2 = &searchInfo2; /* the catalog info mask */ |
/* only match files (not directories) */ |
searchInfo1.nodeFlags = 0; /* kFSNodeIsDirectoryBit set to 0 for files */ |
searchInfo2.nodeFlags = kFSNodeIsDirectoryMask; /* check only this nodeFlag bit */ |
/* search for this fileType */ |
// ((FileInfo *)&searchInfo1.finderInfo)->fileType = 'CWWP'; /* AppleWorks WP files */ |
// ((FileInfo *)&searchInfo2.finderInfo)->fileType = (OSType)0xffffffff; |
/* search for this fileCreator */ |
// ((FileInfo *)&searchInfo1.finderInfo)->fileCreator = 'BOBO'; /* AppleWorks */ |
// ((FileInfo *)&searchInfo2.finderInfo)->fileCreator = (OSType)0xffffffff; |
/* find only visible files */ |
((FileInfo *)&searchInfo1.finderInfo)->finderFlags = 0; |
((FileInfo *)&searchInfo2.finderInfo)->finderFlags = kIsInvisible; |
/* zero all other FileInfo fields */ |
((FileInfo *)&searchInfo1.finderInfo)->location.v = 0; |
((FileInfo *)&searchInfo1.finderInfo)->location.h = 0; |
((FileInfo *)&searchInfo1.finderInfo)->reservedField = 0; |
((FileInfo *)&searchInfo2.finderInfo)->location.v = 0; |
((FileInfo *)&searchInfo2.finderInfo)->location.h = 0; |
((FileInfo *)&searchInfo2.finderInfo)->reservedField = 0; |
windowInfo->searchPB.ioCompletion = NewIOCompletionUPP( CatalogSearchCompletionRoutine ); |
windowInfo->searchPB.iterator = iterator; |
windowInfo->searchPB.searchParams = &searchCriteria; |
windowInfo->searchPB.maximumItems = kMaximumObjects; |
windowInfo->searchPB.whichInfo = kFSCatInfoContentMod; |
windowInfo->searchPB.catalogInfo = catalogInfos; |
windowInfo->searchPB.refs = refs; |
windowInfo->searchPB.specs = specs; |
windowInfo->searchPB.names = names; |
// Install the Carbon Event handler for our simple search window |
err = InstallWindowEventHandler( windowInfo->window, NewEventHandlerUPP( MyThreadWindowEventHandlerProc ), GetEventTypeCount(windowEvents), windowEvents, windowInfo->window, NULL ); |
if ( err != noErr ) SysBeep(0); |
// Event Loop Timer to idle controls 5 times a second. |
(void) InstallEventLoopTimer( GetCurrentEventLoop(), 0, kEventDurationSecond / 5, NewEventLoopTimerUPP( IdleTask ), windowInfo->window, &(windowInfo->threadIdleTimerRef) ); |
ShowWindow( windowInfo->window ); |
do |
{ |
YieldToAnyThread(); // Yield to other threads |
if ( windowInfo->threadDoneProcessing == false ) |
{ |
if ( windowInfo->searchPB.ioResult <= 0 ) |
{ |
// If catalog has changed since the last search, continue anyways until eof. |
// PBCatSearch() return 0 matches when it determines what the catalog has changed. We must call it again. |
PBCatalogSearchAsync( &(windowInfo->searchPB) ); |
if ( windowInfo->numMatches != displayedNumberOfMatches ) |
{ |
NumToString( windowInfo->numMatches, s ); |
controlID.signature = 'Mach'; // Number of matches static text control |
controlID.id = 0; |
GetControlByID( windowInfo->window, &controlID, &control ); |
SetControlData( control, 0, kControlStaticTextTextTag, s[0], s+1 ); // Number of matches |
Draw1Control( control ); |
displayedNumberOfMatches = windowInfo->numMatches; |
} |
} |
} |
else if ( windowInfo->threadIdleTimerRef != NULL ) // Display final results |
{ |
RemoveEventLoopTimer( windowInfo->threadIdleTimerRef ); |
windowInfo->threadIdleTimerRef = NULL; |
controlID.id = 1; |
GetControlByID( windowInfo->window, &controlID, &control ); |
(void) SetControlVisibility( control, true, true ); // Show the "Done" message |
Draw1Control( control ); |
controlID.id = 2; |
GetControlByID( windowInfo->window, &controlID, &control ); |
(void) SetControlVisibility( control, false, true ); // Hide the spinning arrows |
Draw1Control( control ); |
} |
} while( windowInfo->threadDone == false ); // Loop until the window is closed |
DisposeIOCompletionUPP( windowInfo->searchPB.ioCompletion ); |
Bail: |
CFRelease( windowInfo->nameToMatch ); |
DisposePtr( (Ptr) windowInfo ); |
g.numberOfRunningThreads--; // We're done, decrement the total number of threads |
} |
pascal void CatalogSearchCompletionRoutine( ParmBlkPtr paramBlock ) |
{ |
ThreadWindowInfoStruct *windowInfo = (ThreadWindowInfoStruct*) ( ((Ptr)paramBlock) - offsetof(ThreadWindowInfoStruct, searchPB) ); |
if ( windowInfo->searchPB.actualItems > 0 ) // If we found some matches |
windowInfo->numMatches += windowInfo->searchPB.actualItems; |
if ( windowInfo->searchPB.ioResult == errFSNoMoreItems ) // No more matches to be found |
{ |
windowInfo->threadDoneProcessing = true; |
} |
} |
// Carbon Event Handler for our simple thread window |
static pascal OSStatus MyThreadWindowEventHandlerProc( EventHandlerCallRef inCallRef, EventRef inEvent, void* inUserData ) |
{ |
HICommand command; |
EventRef event; |
WindowRef window = (WindowRef) inUserData; |
UInt32 eventClass = GetEventClass( inEvent ); |
UInt32 eventKind = GetEventKind( inEvent ); |
ThreadWindowInfoStruct *windowInfo = (ThreadWindowInfoStruct*) GetWRefCon( window ); |
switch ( eventClass ) |
{ |
case kEventClassCommand: |
if ( eventKind == kEventCommandProcess ) |
{ |
GetEventParameter( inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &command ); |
if ( command.commandID == kHICommandClose ) // <command>-W sends this command |
{ // We turn around and send it as a kEventWindowClose |
(void) CreateEvent( NULL, kEventClassWindow, kEventWindowClose, GetCurrentEventTime(), kEventAttributeUserEvent, &event ); |
(void) SetEventParameter( event, kEventParamDirectObject, typeWindowRef, sizeof(window), &window ); |
(void) SendEventToWindow( event, GetUserFocusWindow() ); |
} |
} |
break; |
case kEventClassWindow: |
if ( eventKind == kEventWindowClose ) |
{ |
windowInfo->threadDone = true; |
} |
break; |
} |
return( eventNotHandledErr ); |
} |
static pascal void IdleTask( EventLoopTimerRef inTimer, void *inUserData ) |
{ |
WindowRef window = inUserData; |
if ( window != NULL ) |
IdleControls( window ); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-07-29