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.
RecentItems.c
// |
// File: RecentItems.c |
// |
// Created: 10/29/05 |
// |
// Contains: Implementation of Recent menu |
// |
// Copyright: Copyright � 2005 Apple Computer, Inc., All Rights Reserved |
// |
// 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. |
// |
//***************************************************** |
#pragma mark - includes & imports |
//---------------------------------------------------- |
#include "RecentItems.h" |
//***************************************************** |
#pragma mark - typedef's, struct's, enums, defines, etc. |
//---------------------------------------------------- |
#define kRecentItemsNameKey CFSTR( "Name" ) // CFString for the name |
#define kRecentItemsAliasKey CFSTR( "Alias" ) // CFData of the alias |
//***************************************************** |
#pragma mark - local ( static ) function prototypes |
//---------------------------------------------------- |
//***************************************************** |
#pragma mark - exported globals |
//---------------------------------------------------- |
//***************************************************** |
#pragma mark - local ( static ) globals |
//---------------------------------------------------- |
static const OSType kRecentMenuPropertyCreator = 'RecM'; |
static const OSType kRecentMenuFilePropertyTag = 'File'; |
static const OSType kRecentMenuMaxItemsPropertyTag = 'MaxI'; |
//***************************************************** |
#pragma mark - exported function implementations |
//---------------------------------------------------- |
/************************************************************************* |
* |
* RecentItems_Update( inPrefKeyCFStringRef, inFSRef, inSynchronizePrefs ) |
* |
* Purpose: Adds or updates a recent item to our prefs |
* |
* Inputs: inPrefKeyCFStringRef - the key to use for this menu's recent preferences |
* inFSRef - RSRef for the recent file/folder |
* inSynchronizePrefs - TRUE to CFPreferencesAppSynchronize (for best performance only TRUE on last item update) |
* |
* Returns: OSStatus - error code ( 0 == no error ) |
*/ |
OSStatus RecentItems_Update( CFStringRef inPrefKeyCFStringRef, const FSRef* inFSRef, Boolean inSynchronizePrefs ) |
{ |
OSStatus result = paramErr; // assume failure ( pessimist! ) |
if ( inPrefKeyCFStringRef && inFSRef ) { |
CFDataRef aliasCFDataRef = NULL; |
CFMutableArrayRef newCFArrayRef = NULL; |
CFStringRef nameCFStringRef = NULL; |
CFURLRef fileCFURLRef = NULL; |
// create an alias from the input FSRef |
AliasHandle tAliasHdl; |
result = FSNewAlias( NULL, inFSRef, &tAliasHdl ); |
require_noerr( result, Oops ); |
result = coreFoundationUnknownErr; // assume failure ( pessimist! ) |
// convert the alias to CFData |
aliasCFDataRef = CFDataCreate( kCFAllocatorDefault, ( UInt8* ) *tAliasHdl, GetHandleSize( ( Handle ) tAliasHdl ) ); |
require( aliasCFDataRef, Oops ); |
// create a CFURL from the input FSRef |
fileCFURLRef = CFURLCreateFromFSRef( kCFAllocatorDefault, inFSRef ); |
require( fileCFURLRef, Oops ); |
// pull out its name |
nameCFStringRef = CFURLCopyLastPathComponent( fileCFURLRef ); |
require( nameCFStringRef, Oops ); |
// now load the recent item preference (array) and make a mutable copy |
CFArrayRef tCFArrayRef = ( CFArrayRef ) CFPreferencesCopyAppValue( inPrefKeyCFStringRef, kCFPreferencesCurrentApplication ); |
if ( tCFArrayRef ) { |
if ( CFArrayGetTypeID( ) == CFGetTypeID( tCFArrayRef ) ) { |
newCFArrayRef = CFArrayCreateMutableCopy( kCFAllocatorDefault, 0, tCFArrayRef ); |
} |
CFRelease( tCFArrayRef ); |
} |
// in case that failed we'll just create an empty mutable array |
if ( !newCFArrayRef ) { |
newCFArrayRef = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks ); |
} |
require( newCFArrayRef, Oops ); |
// Now look thru the array (backwards) to see if our item (name) already exists |
CFIndex idx, cnt = CFArrayGetCount( newCFArrayRef ); |
for ( idx = cnt - 1; idx >= 0; idx-- ) { |
CFDictionaryRef tCFDictionaryRef = ( CFDictionaryRef ) CFArrayGetValueAtIndex( newCFArrayRef, idx ); |
if ( tCFDictionaryRef ) { |
if ( CFDictionaryGetTypeID( ) == CFGetTypeID( tCFDictionaryRef ) ) { |
CFStringRef matchCFStringRef; |
if ( CFDictionaryGetValueIfPresent( tCFDictionaryRef, kRecentItemsNameKey, ( void* ) &matchCFStringRef ) ) { |
if ( kCFCompareEqualTo == CFStringCompare( nameCFStringRef, matchCFStringRef, kCFCompareLocalized ) ) { |
// If so then delete it |
CFArrayRemoveValueAtIndex( newCFArrayRef, idx ); |
} |
} |
} |
} |
} |
// now create our new entry |
CFMutableDictionaryRef tCFMutableDictionaryRef = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks ); |
if ( tCFMutableDictionaryRef ) { |
// add the name and the alias to the new entry |
CFDictionaryAddValue( tCFMutableDictionaryRef, kRecentItemsNameKey, nameCFStringRef ); |
CFDictionaryAddValue( tCFMutableDictionaryRef, kRecentItemsAliasKey, aliasCFDataRef ); |
// and add the new entry to the mutable array |
CFArrayInsertValueAtIndex( newCFArrayRef, 0, tCFMutableDictionaryRef ); |
// and save the mutalbe array as our new preference |
CFPreferencesSetAppValue( inPrefKeyCFStringRef, newCFArrayRef, kCFPreferencesCurrentApplication ); |
CFRelease( tCFMutableDictionaryRef ); |
result = noErr; |
} |
Oops: ; |
if ( newCFArrayRef ) CFRelease( newCFArrayRef ); |
if ( nameCFStringRef ) CFRelease( nameCFStringRef ); |
if ( fileCFURLRef ) CFRelease( fileCFURLRef ); |
if ( aliasCFDataRef ) CFRelease( aliasCFDataRef ); |
} |
if ( inSynchronizePrefs ) { |
if ( !CFPreferencesAppSynchronize( kCFPreferencesCurrentApplication ) ) { |
result = coreFoundationUnknownErr; |
} |
} |
return result; |
} // RecentItems_Update |
/************************************************************************* |
* |
* RecentItems_Clear( inPrefKeyCFStringRef ) |
* |
* Purpose: clears our recent pref |
* |
* Inputs: inPrefKeyCFStringRef - the key to use for this menu's recent preferences |
* |
* Returns: OSStatus - error code ( 0 == no error ) |
*/ |
OSStatus RecentItems_Clear( CFStringRef inPrefKeyCFStringRef ) |
{ |
OSStatus result = paramErr; // assume failure ( pessimist! ) |
if ( inPrefKeyCFStringRef ) { |
CFPreferencesSetAppValue( inPrefKeyCFStringRef, NULL, kCFPreferencesCurrentApplication ); |
( void ) CFPreferencesAppSynchronize( kCFPreferencesCurrentApplication ); |
} |
return result; |
} // RecentItems_Clear |
/************************************************************************* |
* |
* RecentItems_PopulateMenu( inMenuRef, inPrefKeyCFStringRef, inMenuCommand ) |
* |
* Purpose: Populates menu with recent docs from prefs |
* |
* Inputs: inMenuRef - MenuRef for the menu we're to populate |
* inPrefKeyCFStringRef - the key to use for this menu's recent preferences |
* inMenuCommand - the command to use for these items |
* |
* Returns: OSStatus - error code ( 0 == no error ) |
*/ |
OSStatus RecentItems_PopulateMenu( MenuRef inMenuRef, CFStringRef inPrefKeyCFStringRef, MenuCommand inMenuCommand ) |
{ |
OSStatus status, result = paramErr; // assume failure ( pessimist! ) |
require( inMenuRef, Oops ); |
require( inPrefKeyCFStringRef, Oops ); |
result = noErr; |
UInt16 numMenuItems = CountMenuItems( inMenuRef ); |
MenuItemIndex tMenuItemIndex = 0; |
// ( void ) DeleteMenuItems( inMenuRef, 1, CountMenuItems( inMenuRef ) ); // ( ignore errors ) |
// get our preference |
CFArrayRef tCFArrayRef = ( CFArrayRef ) CFPreferencesCopyAppValue( inPrefKeyCFStringRef, kCFPreferencesCurrentApplication ); |
// if the preference exists and is of the correct type ( CFArray )... |
if ( tCFArrayRef && ( CFArrayGetTypeID( ) == CFGetTypeID( tCFArrayRef ) ) ) { |
CFIndex idx, cnt = CFArrayGetCount( tCFArrayRef ); |
UInt16 maxCount; |
status = RecentItems_GetMaxItems( inMenuRef, &maxCount ); |
if ( noErr == status ) { |
if ( cnt > maxCount ) { |
cnt = maxCount; |
} |
} |
// for each element of the array... |
for ( idx = 0; idx < cnt; idx++ ) { |
// get this element of the array... |
CFDictionaryRef tCFDictionaryRef = ( CFDictionaryRef ) CFArrayGetValueAtIndex( tCFArrayRef, idx ); |
// if the element exists and is of the correct type ( CFDictionary )... |
if ( tCFDictionaryRef && ( CFDictionaryGetTypeID( ) == CFGetTypeID( tCFDictionaryRef ) ) ) { |
// ... get the elements name ... |
CFStringRef nameCFStringRef; |
if ( CFDictionaryGetValueIfPresent( tCFDictionaryRef, kRecentItemsNameKey, ( void* ) &nameCFStringRef ) ) { |
// if it's a CFString... |
if ( CFStringGetTypeID( ) == CFGetTypeID( nameCFStringRef ) ) { |
// now get its alias... |
CFDataRef aliasCFDataRef; |
if ( CFDictionaryGetValueIfPresent( tCFDictionaryRef, kRecentItemsAliasKey, ( void* ) &aliasCFDataRef ) ) { |
// if it's of the correct type... |
if ( CFDataGetTypeID( ) == CFGetTypeID( aliasCFDataRef ) ) { |
// now convert the CF data back into an alias |
CFIndex dataSize = CFDataGetLength( aliasCFDataRef ); |
AliasHandle tAliasHdl = ( AliasHandle ) NewHandle( dataSize ); |
if ( tAliasHdl ) { |
CFDataGetBytes( aliasCFDataRef, CFRangeMake( 0, dataSize ), ( UInt8* ) *tAliasHdl ); |
FSRef tFSRef; |
Boolean wasChanged; |
// if we can resolve the alias ... |
status = FSResolveAlias( NULL, tAliasHdl, &tFSRef, &wasChanged ); |
if ( noErr == status ) { // ... then ... |
tMenuItemIndex++; // bump index... |
// if the menu item exists... |
if ( tMenuItemIndex <= numMenuItems ) { |
// set its name |
status = SetMenuItemTextWithCFString( inMenuRef, tMenuItemIndex, nameCFStringRef ); |
if ( noErr == status ) { // and its commandID |
status = SetMenuItemCommandID( inMenuRef, tMenuItemIndex, inMenuCommand ); |
} |
} else { // ... otherwise append a new menu item |
status = AppendMenuItemTextWithCFString( inMenuRef, nameCFStringRef, kMenuItemAttrIgnoreMeta, inMenuCommand, &tMenuItemIndex ); |
} |
// if that succeeded ... |
if ( noErr == status ) { |
// then set the menu item's property |
status = SetMenuItemProperty( inMenuRef, tMenuItemIndex, kRecentMenuPropertyCreator, kRecentMenuFilePropertyTag, sizeof( FSRef ), &tFSRef ); |
if ( noErr == status ) { |
EnableMenuItem( inMenuRef, tMenuItemIndex ); |
} else { |
DisableMenuItem( inMenuRef, tMenuItemIndex ); |
} |
} |
} |
DisposeHandle( ( Handle ) tAliasHdl ); |
} |
if ( noErr == result ) result = status; |
} |
} |
} |
} |
} |
} |
} else { // Invalid pref, delete it |
CFPreferencesSetAppValue( inPrefKeyCFStringRef, NULL, kCFPreferencesCurrentApplication ); |
( void ) CFPreferencesAppSynchronize( kCFPreferencesCurrentApplication ); |
} |
// delete any extra menu items |
if ( tMenuItemIndex < numMenuItems ) { |
status = DeleteMenuItems( inMenuRef, tMenuItemIndex + 1, numMenuItems - tMenuItemIndex ); // ( ignore errors ) |
} |
// if we found our prefernece... |
if ( tCFArrayRef ) { |
CFRelease( tCFArrayRef ); // release it |
} |
Oops: ; |
return result; |
} // RecentItems_PopulateMenu |
/************************************************************************* |
* |
* RecentItems_GetMenuItemFile( inMenuRef, inMenuItemIndex, ioFSRef ) |
* |
* Purpose: returns the FSRef for the specified menu item |
* |
* Inputs: inMenuRef - MenuRef for the recent menu |
* inMenuItemIndex - MenuItemIndex for the item in the menu |
* ioFSRef - address where to put the FSRef for this menu item |
* |
* Returns: OSStatus - error code ( 0 == no error ) |
*/ |
OSStatus RecentItems_GetMenuItemFile( MenuRef inMenuRef, MenuItemIndex inMenuItemIndex, FSRef* ioFSRef ) |
{ |
OSStatus result = paramErr; // assume failure ( pessimist! ) |
if ( inMenuRef && ioFSRef ) { |
result = GetMenuItemProperty( inMenuRef, inMenuItemIndex, kRecentMenuPropertyCreator, kRecentMenuFilePropertyTag, sizeof( FSRef ), NULL, ioFSRef ); |
} |
return result; |
} // RecentItems_GetMenuItemFile |
/************************************************************************* |
* |
* RecentItems_DisableOpenItems( inMenuRef ) |
* |
* Purpose: disable any menu items that are currently open |
* |
* Inputs: inMenuRef - MenuRef for the recent menu |
* |
* Returns: OSStatus - error code ( 0 == no error ) |
*/ |
OSStatus RecentItems_DisableOpenItems( MenuRef inMenuRef ) |
{ |
OSStatus status, result = paramErr; // assume failure ( pessimist! ) |
if ( inMenuRef ) { |
result = noErr; |
UInt16 idx, cnt = CountMenuItems( inMenuRef ); |
for ( idx = 1; idx <= cnt; idx++ ) { |
Boolean enable = FALSE; // assume failure ( pessimist! ) |
FSRef thisFSRef; |
status = RecentItems_GetMenuItemFile( inMenuRef, idx, &thisFSRef ); |
if ( noErr == status ) { |
enable = TRUE; // now assume success! ( optmist! ) |
WindowRef tWindowRef = GetFrontWindowOfClass( kDocumentWindowClass, TRUE ); |
while ( tWindowRef && enable ) { |
FSRef testFSRef; |
status = HIWindowGetProxyFSRef( tWindowRef, &testFSRef ); |
if ( ( noErr == status ) && ( noErr == FSCompareFSRefs( &thisFSRef, &testFSRef ) ) ) { |
enable = FALSE; // this FSRef is the same as one of our windows proxy FSRef |
} |
tWindowRef = GetNextWindowOfClass( tWindowRef, kDocumentWindowClass, TRUE ); |
} |
} |
if ( enable ) { |
EnableMenuItem( inMenuRef, idx ); |
} else { |
DisableMenuItem( inMenuRef, idx ); |
} |
} |
} |
return result; |
} // RecentItems_DisableOpenItems |
/************************************************************************* |
* |
* RecentItems_SetMaxItems( inMenuRef, inMax ) |
* |
* Purpose: Set the maximum number of recent items remembered for this menu |
* |
* Inputs: inMenuRef - MenuRef for the recent menu |
* inMax - the new maximum for this menu |
* |
* Returns: OSStatus - error code ( 0 == no error ) |
*/ |
OSStatus RecentItems_SetMaxItems( MenuRef inMenuRef, UInt16 inMax ) |
{ |
OSStatus result = paramErr; // assume failure ( pessimist! ) |
if ( inMenuRef ) { |
// then set the menu item's property |
result = SetMenuItemProperty( inMenuRef, 0, kRecentMenuPropertyCreator, kRecentMenuMaxItemsPropertyTag, sizeof( UInt16 ), &inMax ); |
// delete any extra menu items |
UInt16 numMenuItems = CountMenuItems( inMenuRef ); |
if ( inMax < numMenuItems ) { |
(void) DeleteMenuItems( inMenuRef, inMax + 1, numMenuItems - inMax + 1 ); // ( ignore errors ) |
} |
} |
return result; |
} // RecentItems_SetMaxItems |
/************************************************************************* |
* |
* RecentItems_GetMaxItems( inMenuRef, outMax ) |
* |
* Purpose: Get the maximum number of recent items remembered for this menu |
* |
* Inputs: inMenuRef - MenuRef for the recent menu |
* |
* Outputs: outMax - where to store the max |
* |
* Returns: OSStatus - error code ( 0 == no error ) |
*/ |
OSStatus RecentItems_GetMaxItems( MenuRef inMenuRef, UInt16* outMax ) |
{ |
OSStatus result = paramErr; // assume failure ( pessimist! ) |
if ( inMenuRef && outMax ) { |
// then get the menu's property |
result = GetMenuItemProperty( inMenuRef, 0, kRecentMenuPropertyCreator, kRecentMenuMaxItemsPropertyTag, sizeof( UInt16 ), NULL, outMax ); |
} |
return result; |
} // RecentItems_SetMaxItems |
//***************************************************** |
#pragma mark - local ( static ) function implementations |
//---------------------------------------------------- |
Copyright © 2006 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2006-10-09