// =========================================================================== // BasicApp.cp PPx Stationery ©2003 Metrowerks Corp. // =========================================================================== #include "BasicApp.h" //#include "MyFrameView.h" #include #include #include #include #include #include #include //#include //#include //#include #include #include #include "NibPrefsDialog.h" #include "NibAboutDialog.h" #include "NibEncodingsDialog.h" #include "CConverter.h" #include "CFObjDel.h" #include "Alert.h" #include "CarbonScrap.h" #include "StAEDesc.h" #include "CThrownResult.h" #include "AMacHandle.h" #include #include "CErrorLog.h" #include "ACFMutableString.h" #include "CStreamConverter.h" #include "AStdNew.h" #include #include "AFileRef.h" #include "GetThrownOSStatus.h" // --------------------------------------------------------------------------- // Function Prototypes void BeginProgram(); void RunProgram(); void EndProgram(); // --------------------------------------------------------------------------- // Constants const SInt16 MBAR_MyMenuBar = 128; const SInt16 ALRT_AboutBox = 128; CyclonePrefs * gPrefs = NULL; CErrorLog * gErrorLog = NULL; bool gMultiFileConversion = false; bool gNoAlerts = false; HFSUniStr255 gCurrFileName = {0,0}; // --------------------------------------------------------------------------- // Begin Program void BeginProgram() { // Program initialization code ::InitCursor(); IBNibRef nibRef = NULL; OSStatus err = CreateNibReference(CFSTR("main"), &nibRef); PPx_ThrowIfOSError_(err, "CreateNibReference failed"); PPx_ThrowIfNil_( nibRef, PPx::DataError, PPx::err_MissingData, "CreateNibReference failed" ); err = SetMenuBarFromNib(nibRef, CFSTR("MenuBar")); DisposeNibReference(nibRef);//dispose nib reference first PPx_ThrowIfOSError_(err, "SetMenuBarFromNib failed"); /* _tk_ // Create Menu Bar MenuBarHandle mbar = ::GetNewMBar(MBAR_MyMenuBar); PPx_ThrowIfNil_( mbar, PPx::DataError, PPx::err_MissingData, "MBAR resource not found" ); ::SetMenuBar(mbar); */ // Register XML Decoders/Encoders PPx::RegisterCommonXMLDecoders(); PPx::RegisterCommonXMLEncoders(); /*_tk_ // Register Persistent Classes PPx_RegisterPersistent_(PPx::Window); PPx_RegisterPersistent_(PPx::WindowContentView); PPx_RegisterPersistent_(PPx::BindingsFrameAdapter); PPx_RegisterPersistent_(PPx::StaticText); PPx_RegisterPersistent_(MyFrameView); */ } // --------------------------------------------------------------------------- // RunProgram void RunProgram() { BasicApp theApp; // Create application object // Send command to make a new window // PPx::EventUtils::PostCommandID(kHICommandNew); theApp.Run(); // Run the application event loop } // --------------------------------------------------------------------------- // End Program void EndProgram() { // Clean up before program terminates } #pragma mark - // --------------------------------------------------------------------------- // BasicApp Default Contructor BasicApp::BasicApp() : mInputTextEncoding(kTextEncodingMacRoman), mOutputTextEncoding(kTextEncodingMacRoman), mLineBreak(kLineBreakNoChange), mAskForConversion (true), mStopMultipleConversion(false) { mInputBuffer.SetOutputBuffer(&mOutputBuffer); // Install event handlers EventTargetRef targetRef = GetSysEventTarget(); CommandConverter::Install(targetRef); SpecificMenuCommandEnableDoer::Install(targetRef); SpecificMenuCommandDoer::Install(targetRef); SpecificMenuCommandDoer::Install(targetRef); CommandHandler::Install(targetRef); CommandHandler::Install(targetRef); mPrefs.Read(); gPrefs = &(mPrefs.mPrefs); //this is critical to our application that is why we call it so early: if( CConverter::GetAllEncodingMappings() == false ) { StopAlertResID(2, kTECMissingTableErr);//Cannot get Text Encoding Converter maps. throw kTECMissingTableErr;//kill the app } } BasicApp::~BasicApp() { mPrefs.Save(); gPrefs = NULL; } // --------------------------------------------------------------------------- // ClassName Persistent Name CFStringRef BasicApp::ClassName() const { return CFSTR("BasicApp"); // Return class name as a string } // --------------------------------------------------------------------------- // DoSpecificCommand OSStatus BasicApp::DoSpecificCommand( PPx::CommandIDType, PPx::SysCarbonEvent& /* ioEvent */) { NibAboutDialog aboutDlog(CFSTR("main"), CFSTR("AboutWindow")); aboutDlog.Run(); return noErr; } // --------------------------------------------------------------------------- // DoSpecificCommand OSStatus BasicApp::DoSpecificCommand( PPx::CommandIDType, PPx::SysCarbonEvent& /* ioEvent */) { NibPrefsDialog prefsDlog(CFSTR("main"), CFSTR("Preferences")); prefsDlog.SetInitialDialogOptions(mPrefs.mPrefs); prefsDlog.Run(); if(prefsDlog.WasDialogOkeyed()) { prefsDlog.GetClosingDialogOptions(mPrefs.mPrefs); mPrefs.Save(); } return noErr; } // --------------------------------------------------------------------------- // DoSpecificCommand OSStatus BasicApp::DoSpecificCommand( PPx::CommandIDType, PPx::SysCarbonEvent& /* ioEvent */) { NavDialogCreationOptions creationOptions; PPx::NavServices::GetDefaultCreationOptions(creationOptions); creationOptions.clientName = (CFStringRef) PPx::PrimaryBundle::Instance().GetValueForInfoDictionaryKey(kCFBundleNameKey); creationOptions.optionFlags = kNavDefaultNavDlogOptions | kNavDontAutoTranslate | kNavNoTypePopup | kNavAllowMultipleFiles; PPx::NavServices::AskGetFile(*this, (NavTypeListHandle) NULL, creationOptions); return noErr; } // --------------------------------------------------------------------------- // DoSpecificCommandStatus OSStatus BasicApp::DoSpecificCommandStatus( PPx::CommandIDType, PPx::SysCarbonEvent& /* ioEvent */) { bool enable = true; PPx::EventUtils::SetMenuCommandStatus( cmd_Convert, enable ); return noErr; } // --------------------------------------------------------------------------- // DoSpecificCommand OSStatus BasicApp::DoSpecificCommand( PPx::CommandIDType, PPx::SysCarbonEvent& /* ioEvent */) { //temp #if 0 gMultiFileConversion = false; gCurrFileName[0] = 0; #endif TextEncoding inputEncoding = kTextEncodingMacRoman; TextEncoding outputEncoding; if( (mPrefs.mPrefs.flags & kUseSniffers) != 0 ) { TextEncoding suggestedEncoding; if( CConverter::SniffClipboardContent(suggestedEncoding) ) inputEncoding = suggestedEncoding; } CFStringRef sourceName = ::CFCopyLocalizedStringFromTableInBundle( CFSTR("Clipboard"), CFSTR("Localizable"), ::CFBundleGetMainBundle(), ""); CFObjDel strDel(sourceName); UInt16 lineBreak = kLineBreakNoChange; if( AskForConversionType(inputEncoding, outputEncoding, lineBreak, sourceName) ) { OSStatus status = noErr; //release memory occupied by our buffers (they will restore it when needed) mInputBuffer.Free(); mOutputBuffer.Free(); try { //all alerts are done inside except for some throws status = CConverter::ConvertClipboard( inputEncoding, outputEncoding, lineBreak); } catch(...) { OSStatus thrownErr = GetThrownOSStatus(); StopAlertResID(4, thrownErr);//Clipboard conversion failed. } } return noErr; } // --------------------------------------------------------------------------- // DoSpecificCommandStatus OSStatus BasicApp::DoSpecificCommandStatus( PPx::CommandIDType, PPx::SysCarbonEvent& /* ioEvent */) { bool isUnicode = false; bool enable = ClipboardHasText(isUnicode); PPx::EventUtils::SetMenuCommandStatus( cmd_ConvertClipboard, enable ); return noErr; } #pragma mark - // --------------------------------------------------------------------------- // DoAEOpenDocuments OSStatus BasicApp::DoAEOpenDocuments( const PPx::AutoAEDesc& inAppleEvent, PPx::AutoAEDesc& /* outAEReply */) { PPx::AutoAEDesc docList( inAppleEvent.GetRequiredParamDesc(keyDirectObject) ); OpenListOfDocuments(docList); return noErr; } OSStatus BasicApp::DoAEConvertFiles(const AppleEvent& inAppleEvent, AEDesc& outResult) { OSStatus outStatus = noErr; try { StAEDesc docList; CThrownOSStatus status = ::AEGetParamDesc(&inAppleEvent, keyDirectObject, typeAEList, docList); status = ::AECreateList( NULL, 0, false, &outResult ); // DescType theType; // Size theSize; mInputTextEncoding = GetEncodingFromAppleEventParam(inAppleEvent, FOUR_CHAR_CODE('from') ); mOutputTextEncoding = GetEncodingFromAppleEventParam(inAppleEvent, FOUR_CHAR_CODE('to ') ); mLineBreak = kLineBreakNoChange; GetLineBreakOptionFromAppleEventParam( inAppleEvent, mLineBreak ); SInt32 numDocs; status = ::AECountItems(docList, &numDocs); // Loop through all items in the list // Extract descriptor for the document // Coerce descriptor data into a FSSpec // Tell Program object to open document mAskForConversion = false;//do not ask for conversion mStopMultipleConversion = false; //reset value if(gErrorLog != NULL) { delete gErrorLog; gErrorLog = NULL; } if( numDocs > 1 ) { gMultiFileConversion = true; if( (mPrefs.mPrefs.flags & kSuppressMsgGenLog) ) gErrorLog = new CErrorLog(); } else gMultiFileConversion = false; OSStatus err = noErr; FSRef fileRef; for (SInt32 i = 1; i <= numDocs; i++) { AEKeyword theKey; StAEDesc theFile; err = ::AEGetNthDesc(docList, i, typeWildCard, &theKey, theFile); if(err == noErr ) { try { if( GetFSRefFromAEDesc(theFile, fileRef) == noErr ) { ProcessOneFile(fileRef); } } catch(...) { //nothing here - all alerts should be done before } StAEDesc fileDesc; CreateAliasDesc( &fileRef, (AEDesc*)fileDesc); err = ::AEPutDesc( &outResult, 0, (AEDesc*)fileDesc );//add to end if( mStopMultipleConversion ) break; } } if(gErrorLog != NULL) { try { gErrorLog->SaveLog(fileRef); } catch(...) { //alert? } delete gErrorLog; } gErrorLog = NULL; } catch(...) { outStatus = GetThrownOSStatus(); } return outStatus; } OSStatus BasicApp::DoAEConvertClipboard(const AppleEvent& inAppleEvent, AEDesc& outResult) { #pragma unused (outResult) OSStatus outStatus = noErr; try { mInputTextEncoding = GetEncodingFromAppleEventParam(inAppleEvent, FOUR_CHAR_CODE('from') ); mOutputTextEncoding = GetEncodingFromAppleEventParam(inAppleEvent, FOUR_CHAR_CODE('to ') ); mLineBreak = kLineBreakNoChange; GetLineBreakOptionFromAppleEventParam( inAppleEvent, mLineBreak ); gMultiFileConversion = false; gCurrFileName.length = 0; //release memory occupied by our buffers (they will restore it when needed) mInputBuffer.Free(); mOutputBuffer.Free(); CThrownOSStatus status = CConverter::ConvertClipboard( mInputTextEncoding, mOutputTextEncoding, mLineBreak);//this may throw, but it is OK to throw from here } catch(...) { outStatus = GetThrownOSStatus(); } return outStatus; } OSStatus BasicApp::DoAEConvertText(const AppleEvent& inAppleEvent, AEDesc& outResult) { OSStatus outStatus = noErr; try { DescType theType = typeChar; StAEDesc inputText; CThrownOSStatus status = ::AEGetParamDesc(&inAppleEvent, keyDirectObject, typeWildCard, inputText); if(inputText.GetDataStorage() == NULL) { unsigned char noData = 0; status = ::AECreateDesc(typeChar, &noData, 0, &outResult); return status; } mInputTextEncoding = GetEncodingFromAppleEventParam(inAppleEvent, FOUR_CHAR_CODE('from') ); mOutputTextEncoding = GetEncodingFromAppleEventParam(inAppleEvent, FOUR_CHAR_CODE('to ') ); mLineBreak = kLineBreakNoChange; GetLineBreakOptionFromAppleEventParam( inAppleEvent, mLineBreak ); gMultiFileConversion = false; gCurrFileName.length = 0; //release memory occupied by our buffers (they will restore it when needed) mInputBuffer.Free(); mOutputBuffer.Free(); Size theSize = ::AEGetDescDataSize( (const AEDesc*)inputText ); Handle srcH = ::NewHandle( theSize ); status = ::MemError(); StHandleLocker srcLock(srcH); ::AEGetDescData( (const AEDesc*)inputText, *srcH, theSize ); srcLock.Release(); Handle destH = ::NewHandle( theSize ); status = ::MemError(); // if(destH != nil) // { AHandle srcDel(destH); AHandle destDel(destH); status = CConverter::ConvertFromOneEncodingToAnother( mInputTextEncoding, mOutputTextEncoding, srcH, destH); //line breaks correction ByteCount destLen = ::GetHandleSize(destH); ByteCount destBuffSize = destLen; if(mLineBreak == kLineBreakAutoChange) { TextEncodingBase theBase = ::GetTextEncodingBase(mOutputTextEncoding); if((theBase >= kStandardDOS) && (theBase < kStandardOther)) { destBuffSize = 2*destLen; ::SetHandleSize(destH, destBuffSize ); status = ::MemError(); } StHandleLocker destLock(destH); CConverter::AutoCorrectBreaks( (TextPtr)*destH, destLen, destBuffSize, mOutputTextEncoding); } else { if(mLineBreak == kLineBreakWindows) { destBuffSize = 2*destLen; ::SetHandleSize(destH, destBuffSize ); status = ::MemError(); } StHandleLocker destLock(destH); CConverter::ChangeBreaks( (TextPtr)*destH, destLen, destBuffSize, mOutputTextEncoding, mLineBreak); } if( destLen != ::GetHandleSize(destH) ) {//the buffer may be longer after line break change - cut it now ::SetHandleSize(destH, destLen ); status = ::MemError(); } if( CConverter::IsTwoByteUnicode( mOutputTextEncoding ) ) theType = typeUnicodeText; else theType = typeChar; StHandleLocker destLock(destH); status = ::AECreateDesc(theType, *destH, ::GetHandleSize(destH), &outResult); destLock.Release(); // } } catch(...) { outStatus = GetThrownOSStatus(); } return outStatus; } OSStatus BasicApp::DoAESetAlertMode(const AppleEvent& inAppleEvent, AEDesc& outResult) { OSStatus outStatus = noErr; try { DescType theType; Size theSize; unsigned long mode = 0x00000001;//init to show alerts with no log CThrownOSStatus status = ::AEGetParamPtr(&inAppleEvent, keyDirectObject, typeEnumerated, &theType, &mode, sizeof(long), &theSize); if( (mode & 0x00000001) == 0x00000001) //show alerts gNoAlerts = false; else gNoAlerts = true; if( (mode & 0x00000002) == 0x00000002) //do log for multiple files mPrefs.mPrefs.flags |= kSuppressMsgGenLog; else mPrefs.mPrefs.flags &= ~kSuppressMsgGenLog; } catch(...) { outStatus = GetThrownOSStatus(); } return outStatus; } TextEncoding BasicApp::GetEncodingFromAppleEventParam(const AppleEvent& inAppleEvent, AEKeyword inKey) { TextEncoding enc; DescType theType; Size theSize; OSErr err = ::AEGetParamPtr(&inAppleEvent, inKey, typeEnumerated, &theType, &enc, sizeof(long), &theSize); if( err == noErr ) { if(enc == kTextEncodingUnicodeDefault) enc = ::ResolveDefaultTextEncoding(kTextEncodingUnicodeDefault); return enc; } err = ::AEGetParamPtr(&inAppleEvent, inKey, typeLongInteger, &theType, &enc, sizeof(long), &theSize); if( err == noErr) { if(enc == kTextEncodingUnicodeDefault) enc = ::ResolveDefaultTextEncoding(kTextEncodingUnicodeDefault); return enc; } Str255 encInternetName; err = ::AEGetParamPtr(&inAppleEvent, inKey, typeChar, &theType, &(encInternetName[1]), sizeof(Str255)-1, &theSize); if( (err == noErr) && (theSize > 0) && (theSize < 255) ) { encInternetName[0] = (unsigned char)theSize; if(theSize == 6)//check for UTF-16, which is not resolved properly by TECGetTextEncodingFromInternetName { const Str255 utf16Name = "\pUTF-16"; if( 0 == ::RelString( encInternetName, utf16Name, false, true ) )//ignore case, but not diacritical marks { enc = ::ResolveDefaultTextEncoding(kTextEncodingUnicodeDefault); return enc; } } OSStatus status = ::TECGetTextEncodingFromInternetName( &enc, encInternetName); if(status == noErr) { if(enc == kTextEncodingUnicodeDefault) enc = ::ResolveDefaultTextEncoding(kTextEncodingUnicodeDefault); return enc; } } return 0xFFFFFFFF; } void BasicApp::GetLineBreakOptionFromAppleEventParam( const AppleEvent& inAppleEvent, UInt16 &outLineBreak ) { UInt32 lineBreak; DescType theType; Size theSize; OSErr err = ::AEGetParamPtr( &inAppleEvent, 'with', typeEnumerated, &theType, &lineBreak, sizeof(UInt32), &theSize); if( err == noErr ) { outLineBreak = (UInt16)lineBreak; } } #pragma mark - // --------------------------------------------------------------------------- // DoNavUserAction void BasicApp::DoNavUserAction( NavCBRecPtr inParams) { switch (inParams->userAction) { case kNavUserActionOpen: // User wants to open a file DoNavOpenDocument(inParams); break; // Messages for Review // Documents when quitting case kNavUserActionReviewDocuments: break; case kNavUserActionDiscardDocuments: //DoQuit(); break; case kNavUserActionCancel: break; case kNavUserActionSaveAs: break; } } // --------------------------------------------------------------------------- // DoNavOpenDocument void BasicApp::DoNavOpenDocument( NavCBRecPtr inParams) { PPx::AutoNavReply navReply(inParams->context); PPx::AutoAEDesc docList(navReply.Get().selection); OpenListOfDocuments(docList); } // --------------------------------------------------------------------------- // OpenListOfDocuments void BasicApp::OpenListOfDocuments( const PPx::AutoAEDesc& inDocList) { SInt32 numberOfFiles = inDocList.GetCount(); mAskForConversion = true;//ask for conversion at least once! mStopMultipleConversion = false; //reset value if(gErrorLog != NULL) { delete gErrorLog; gErrorLog = NULL; } if( numberOfFiles > 1 ) { gMultiFileConversion = true; if( (gPrefs->flags & kSuppressMsgGenLog) != 0) gErrorLog = new CErrorLog(); } else gMultiFileConversion = false; FSRef lastFileRef; ::memset(&lastFileRef, 0, sizeof(FSRef)); for (SInt32 i = 1; i <= numberOfFiles; i++) { try { FSRef fileRef; ::memset(&fileRef, 0, sizeof(FSRef)); inDocList.GetNthItem(i, fileRef); ProcessOneFile(fileRef); lastFileRef = fileRef; } catch (...) { } if(mStopMultipleConversion) break; } if(gErrorLog != NULL) { try { gErrorLog->SaveLog(lastFileRef); } catch(...) { //alert? } delete gErrorLog; } gErrorLog = NULL; } /* // --------------------------------------------------------------------------- // OpenOneDocument void BasicApp::OpenOneDocument( const FSRef& inFileRef) { FSRef duplicateFileRef = inFileRef; ProcessOneFile(duplicateFileRef); } */ #pragma mark - void BasicApp::ProcessOneFile(FSRef &ioRef) { FSRef theOrigRef = ioRef;//copy original ref //zero the output spec - in case of any error it will stay like this - invalid ::memset(&ioRef, 0, sizeof(ioRef)); gCurrFileName.length = 0; FSCatalogInfo theFileInfo; OSErr err = ::FSGetCatalogInfo(&theOrigRef, kFSCatInfoTextEncoding, &theFileInfo, &mCurrFileName, NULL, &mCurrParentRef); gCurrFileName = mCurrFileName; mCurrFileNameEncodingHint = theFileInfo.textEncodingHint; if( mAskForConversion ) { if( (gPrefs->flags & kAskEncForEachFile) == 0 ) mAskForConversion = false;//do not ask next time you enter here! if( (gPrefs->flags & kUseSniffers) != 0 ) { TextEncoding suggestedEncoding; if( CConverter::SniffFileContent( theOrigRef, suggestedEncoding) ) mInputTextEncoding = suggestedEncoding; } ACFString fileName(mCurrFileName.unicode, mCurrFileName.length); if( false == AskForConversionType(mInputTextEncoding, mOutputTextEncoding, mLineBreak, fileName) ) { mStopMultipleConversion = true; return; } } { FSRef newFileRef; if( SaveFileAs( newFileRef, theOrigRef ) )//creates new empty file { CStreamConverter *theConverter = nil; AStdNew theDel; try { theConverter = new CStreamConverter(mInputTextEncoding, mOutputTextEncoding, mLineBreak, theOrigRef, newFileRef, &mInputBuffer, &mOutputBuffer); theDel.Reset(theConverter); } catch(...) { OSStatus thrownStatus = GetThrownOSStatus(); if( kAlertCancel == StopAlertResIDName(5, thrownStatus ) )//Could not create the converter for the given encodings. mStopMultipleConversion = true; throw; } try { theConverter->Convert(); } catch(...) { OSStatus thrownStatus = GetThrownOSStatus(); if( kAlertCancel == StopAlertResIDName(6, thrownStatus ) )//Conversion failed. mStopMultipleConversion = true; throw; } ioRef = newFileRef;//copy output spec } else mStopMultipleConversion = true; } } #pragma mark - bool BasicApp::ClipboardHasText(bool &outIsUnicode) { outIsUnicode = false; bool hasData = false; try { ScrapRef currScrap = CarbonScrap::GetCurrentScrap(); hasData = CarbonScrap::HasData( currScrap, typeText); if( !hasData ) { hasData = CarbonScrap::HasData( currScrap, typeUnicodeText); if( hasData ) { outIsUnicode = true;//unicode found! } } } catch(...) { } return hasData; } bool BasicApp::AskForConversionType(TextEncoding &inputEncoding, TextEncoding &outputEncoding, UInt16 &outLineBreak, CFStringRef inFileName) { TextEncoding defInputEncoding = kTextEncodingMacRoman; TextEncoding defOutputEncoding = kTextEncodingMacRoman; UInt16 defLineBreak = kLineBreakNoChange; if(CConverter::sAllTextEncodingsArr == NULL) return false; if( (mPrefs.mPrefs.flags & kSaveLastEncodings) != 0) { defInputEncoding = mPrefs.mPrefs.inputEncoding; defOutputEncoding = mPrefs.mPrefs.outputEncoding; defLineBreak = mPrefs.mPrefs.lineBreak; } if( (mPrefs.mPrefs.flags & kUseSniffers) != 0 ) { defInputEncoding = inputEncoding; } NibEncodingsDialog theDlg(CFSTR("main"), CFSTR("Encodings")); theDlg.SetInitialDialogOptions(defInputEncoding, defOutputEncoding, defLineBreak, inFileName); theDlg.Run(); if(theDlg.WasDialogOkeyed()) { theDlg.GetClosingDialogOptions(inputEncoding, outputEncoding, outLineBreak); if( (mPrefs.mPrefs.flags & kSaveLastEncodings) != 0 ) { mPrefs.mPrefs.inputEncoding = inputEncoding; mPrefs.mPrefs.outputEncoding = outputEncoding; mPrefs.mPrefs.lineBreak = outLineBreak; } return true; } return false; } OSErr BasicApp::GetFSSpecFromAEDesc(const AEDesc &inDesc, FSSpec &outSpec) { OSErr err = noErr; if( inDesc.descriptorType == typeFSS ) {//no need to coerce err = ::AEGetDescData( &inDesc, &outSpec, sizeof(FSSpec) ); } else { StAEDesc coercedSpec; err = ::AECoerceDesc( &inDesc, typeFSS, coercedSpec ); if(err == noErr) { err = ::AEGetDescData( coercedSpec, &outSpec, sizeof(FSSpec) ); } } return err; } OSErr BasicApp::GetFSRefFromAEDesc(const AEDesc &inDesc, FSRef &outRef) { OSErr err = noErr; if( inDesc.descriptorType == typeFSRef ) {//no need to coerce err = ::AEGetDescData( &inDesc, &outRef, sizeof(FSRef) ); } else { StAEDesc coercedRef; err = ::AECoerceDesc( &inDesc, typeFSRef, coercedRef ); if(err == noErr) { err = ::AEGetDescData( coercedRef, &outRef, sizeof(FSRef) ); } else {// Cannot get an FSRef. Try getting an FSSpec and make an FSRef out of it. FSSpec theSpec; err = GetFSSpecFromAEDesc(inDesc, theSpec); if(err == noErr) { err = ::FSpMakeFSRef( &theSpec, &outRef ); } } } return err; } OSErr BasicApp::CreateAliasDesc( const AliasHandle inAliasH, AEDesc *outAliasAEDesc ) { OSErr err = noErr; char handleState = ::HGetState( (Handle)inAliasH ); ::HLock( (Handle)inAliasH ); err = ::AECreateDesc( typeAlias, *inAliasH, ::GetHandleSize( (Handle)inAliasH ), outAliasAEDesc ); ::HSetState( (Handle)inAliasH, handleState ); return err; } OSErr BasicApp::CreateAliasDesc( const FSRef *inFSRef, AEDesc *outAliasAEDesc ) { OSErr err = noErr; AliasHandle aliasHandle; err = ::FSNewAlias( NULL, inFSRef, &aliasHandle ); if( (err == noErr) && (aliasHandle == NULL) ) { err = paramErr; } if( err == noErr ) { err = CreateAliasDesc( aliasHandle, outAliasAEDesc ); ::DisposeHandle( (Handle)aliasHandle ); } return err; } Boolean BasicApp::SaveFileAs(FSRef &outFSRef, const FSRef &inFSRef) { Boolean isOK = false; Boolean replacing = false; OSStatus err = noErr; OSType theFileType = kTextFileType; if( (gPrefs->flags & kSaveDontAsk) != 0 ) { outFSRef = inFSRef; if( MakeNewRef( outFSRef ) ) return true; //else - we coud not create the different file name - fall back to save-as } ACFMutableString inputNameRef(mCurrFileName.unicode, mCurrFileName.length ); inputNameRef.Append("¥"); /* StrFileName tempName; LString::CopyPStr(inputSpec->name, tempName, sizeof(StrFileName) ); if(tempName[0] < 31) tempName[0]++; tempName[ tempName[0] ] = '¥'; */ NavDialogCreationOptions creationOptions; PPx::NavServices::GetDefaultCreationOptions(creationOptions); creationOptions.saveFileName = inputNameRef; creationOptions.clientName = (CFStringRef) PPx::PrimaryBundle::Instance().GetValueForInfoDictionaryKey(kCFBundleNameKey); creationOptions.optionFlags = kNavDefaultNavDlogOptions | kNavNoTypePopup | kNavDontAutoTranslate; creationOptions.windowTitle = CFSTR("Save File As"); creationOptions.modality = kWindowModalityAppModal; NavDialogRef dialogRef = NULL; err = ::NavCreatePutFileDialog(&creationOptions, theFileType, kAppSignature, NULL, NULL, &dialogRef); if( (err == noErr) && (dialogRef != NULL) ) { err = ::NavDialogRun( dialogRef ); if( err == noErr ) { NavUserAction theAction = ::NavDialogGetUserAction( dialogRef ); if( (theAction != kNavUserActionCancel) && (theAction != kNavUserActionNone) ) { NavReplyRecord reply; err = ::NavDialogGetReply(dialogRef, &reply); if(err == noErr) { FSRef parentFSRef; AEKeyword theAEKeyword; DescType typeCode; Size actualSize; err = ::AEGetNthPtr( &(reply.selection), 1, typeFSRef, &theAEKeyword, &typeCode, &parentFSRef, sizeof(FSRef), &actualSize); if(err == noErr) { if(reply.saveFileName != NULL) { ACFString saveNameRef(reply.saveFileName); CFRange wholeRange = {0, 0}; wholeRange.length = saveNameRef.Length(); if(wholeRange.length > 255) wholeRange.length = 255; HFSUniStr255 saveName; saveName.length = wholeRange.length; saveNameRef.GetCharacters(wholeRange, saveName.unicode); //TODO //need to compare parent folders and names to prevent self-replace mCurrFileNameEncodingHint = ::CFStringGetSystemEncoding(); ::UpgradeScriptInfoToTextEncoding(reply.keyScript, kTextLanguageDontCare, kTextRegionDontCare, NULL, &mCurrFileNameEncodingHint); FSRef newRef; err = ::FSMakeFSRefUnicode( &parentFSRef, saveName.length, saveName.unicode, mCurrFileNameEncodingHint, &newRef); if( (err == noErr) && reply.replacing) {//file exists and we want to replace it - delete the original ::FSDeleteObject(&newRef); err = fnfErr; } if(err == fnfErr) { FSCatalogInfo fileInfo; fileInfo.textEncodingHint = mCurrFileNameEncodingHint; err = ::FSCreateFileUnicode( &parentFSRef, saveName.length, saveName.unicode, kFSCatInfoTextEncoding, &fileInfo, &newRef, NULL); if(err == noErr) { outFSRef = newRef; isOK = true; } } } } ::NavDisposeReply( &reply ); } } } ::NavDialogDispose( dialogRef ); } return isOK; /* PP_StandardDialogs::LFileDesignator designator; designator.SetFileType( theFileType ); // NavDialogOptions* options = designator.GetDialogOptions(); // if(options != nil) // { // options->dialogOptionFlags = kNavDefaultNavDlogOptions | kNavNoTypePopup | kNavDontAutoTranslate; // ::GetIndString( options->windowTitle, STRx_Standards, 2);//save file as // } isOK = designator.AskDesignateFile( tempName ); AFileRef outputFile(outFSRef); HFSUniStr255 outputName; outputFile.GetUnicodeName(outputName); if (isOK) { designator.GetFileSpec( *outSpec ); replacing = designator.IsReplacing(); if(replacing) { inputFile.FileRefEqual( const AFileRef&inRef) const if( LFile::EqualFileSpec( *outSpec, *inputSpec) ) { StopAlertResIDName(7, dupFNErr);//Source and destination must be different. return false; } ::FSDeleteObject(&outFSRef); if( status != noErr && status != fnfErr) { ACFString fileNameRef(outputName.length, outputName.unicode); Str255 pascalName; fileNameRef.GetPascalString(pascalName); StopAlertResIDStr(8, pascalName, status); return false;//could not remove file! } } } return isOK; */ } Boolean BasicApp::MakeNewRef(FSRef &ioFSRef) { // AFileRef inputFile(ioFSRef); HFSUniStr255 inputName; // inputFile.GetUnicodeName(inputName); FSRef parentRef; ::memset(&parentRef, 0, sizeof(FSRef)); FSCatalogInfo fileInfo; OSErr err = ::FSGetCatalogInfo(&ioFSRef, kFSCatInfoTextEncoding, &fileInfo, &inputName, NULL, &parentRef); if(err != noErr) return false; HFSUniStr255 tempName = inputName; FSRef newRef; ::memset(&newRef, 0, sizeof(FSRef)); UInt16 i = tempName.length; UInt16 j = i; do { if(i < 255) { i++; tempName.unicode[i-1] = 0x2022;//add bullet to the end tempName.length = i; } else if( j >= 1 ) { tempName.unicode[j-1] = 0x2022; //replace chars beginning from the end with bullet j--; } err = ::FSMakeFSRefUnicode( &parentRef, tempName.length, tempName.unicode, fileInfo.textEncodingHint, &newRef); } while( (err == noErr) && ((i < 255) || (j > 0) ) );//we are waiting for fnfErr ! if(err == fnfErr) {//we must create new empty file to have FRef available err = FSCreateFileUnicode( &parentRef, tempName.length, tempName.unicode, kFSCatInfoTextEncoding, &fileInfo, &newRef, NULL); if(err == noErr) { ioFSRef = newRef; return true; } } return false; }