spider e3fbeeb7de Normandy checkin for MacFE ... woohoo!
git-svn-id: svn://10.0.0.236/trunk@4261 18797224-902f-48f8-a5cc-f745e15eee43
1998-06-23 01:36:59 +00:00

490 lines
14 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
// ulaunch.cp
// Launching of external applications through AppleEvents
// Created by atotic, June 14th, 1994
// Based on Apple's LaunchWithDoc example snippet
#include <Folders.h>
#include <AERegistry.h>
#include <Errors.h>
#include "BufferStream.h"
#include "PascalString.h"
#include "macutil.h"
#include "uprefd.h"
#include "ufilemgr.h"
#include "uerrmgr.h"
#include "reserr.h"
#include "ulaunch.h"
// ¥¥ PROTOTYPES
OSErr FindAppOnVolume(OSType sig, short vRefNum, FSSpec& thefile);
// Sends an ODOC event to creator in fndrInfo,
// with file in fileSpec
OSErr SendODOCEvent(OSType appSig,
LFileBufferStream * inFile);
// Launches the application with the given doc
void LaunchWithDoc(FInfo& fndrInfo,
FSSpec& appSpec,
LFileBufferStream * inFile,
const FSSpec inFileSpec);
// Displays a launching error alert.
int LaunchError(ResIDT alertID, OSType creator, const Str63& fileName, OSErr err);
// Creates Finder's OpenSelection event
OSErr BuildOpenSelectionEvent(FSSpec & fileSpec, AppleEvent& theEvent);
// Sends OpenSelection to Finder
OSErr SendOpenSelectionToFinder(FSSpec & fileSpec);
// ¥¥ Implementation
// Builds an ODOC event
OSErr BuildODOCEvent(OSType applSig,
FSSpec fileSpec,
AppleEvent& theEvent) {
// Builds all the arguments for the event
AEDesc myAddress;
AEDesc docDesc;
AEDescList theList;
AliasHandle withThis;
OSErr err;
// Anatomy of the event:
// Event class: kCoreEventClass
// Event ID: kAEOpenDocuments
// Event has target description (in the form of typeApplSignature)
// keyDirectObject is a list of aliases
err = AECreateDesc(typeApplSignature,
(Ptr)&applSig, sizeof(applSig),
&myAddress);
if (err) return err;
err = AECreateAppleEvent(kCoreEventClass, kAEOpenDocuments,
&myAddress,
kAutoGenerateReturnID, kAnyTransactionID,
&theEvent);
if (err) return err;
// create a list for the alaises. In this case, I only have one, but
// you still need a list
err = AECreateList(NULL, 0, FALSE, &theList);
if (err) return err;
/* create an alias out of the file spec */
/* I'm not real sure why I did this, since there is a system coercion handler for */
/* alias to FSSpec, but I'm paranoid */
err = NewAlias(NULL, &fileSpec, &withThis);
if (err) return err;
HLock((Handle)withThis);
/* now create an alias descriptor */
err = AECreateDesc(typeAlias, (Ptr) * withThis, GetHandleSize((Handle)withThis), &docDesc);
if (err) return err;
HUnlock((Handle)withThis);
/* put it in the list */
err = AEPutDesc(&theList, 0, &docDesc);
if (err) return err;
err = AEPutParamDesc(&theEvent, keyDirectObject, &theList);
err = AEDisposeDesc(&myAddress);
err = AEDisposeDesc(&docDesc);
err = AEDisposeDesc(&theList);
return err;
}
// Sends an ODOC event to appliation
OSErr SendODOCEvent(OSType appSig,
LFileBufferStream * inFile)
{
OSErr err;
Try_ {
AppleEvent openEvent;
FSSpec inFileSpec;
inFile->GetSpecifier(inFileSpec);
err = BuildODOCEvent(appSig,
inFileSpec,
openEvent);
ThrowIfOSErr_(err);
AppleEvent result;
err = AESend(&openEvent, &result,
kAENoReply + kAECanSwitchLayer,
kAENormalPriority, kAEDefaultTimeout,
NULL,NULL);
AEDisposeDesc(&openEvent);
// err could be memFullErr, app is out of memory
ThrowIfOSErr_(err);
}
Catch_(inErr) {
return inErr;
} EndCatch_
return err;
}
#define kDelete 2
#define kSave 1
#define kTryAgain 3
// Displays launch error dialogs with appropriate arguments.
// Alerts used are:
// ALRT_ODOCFailed
// ALRT_AppNotFound
// ALRT_AppMemFull
// ALRT_MiscLaunchError
// Returns: kDelete, kSave, or kTryAgain
int LaunchError(ResIDT alertID, OSType creator, const Str63& fileName, OSErr err)
{
CMimeMapper * map = CPrefs::sMimeTypes.FindCreator(creator);
ErrorManager::PrepareToInteract();
CStr255 errorString;
ErrorManager::OSNumToStr(err, errorString);
ParamText(map->GetAppName(), fileName, errorString, "\p");
UDesktop::Deactivate();
int retVal = ::CautionAlert(alertID, NULL);
UDesktop::Activate();
return retVal;
}
// Launches an application with a given doc
OSErr StartDocInApp(FSSpec theDocument, FSSpec theApplication)
{
FInfo fndrInfo;
OSErr err;
HGetFInfo( theApplication.vRefNum,
theApplication.parID,
theApplication.name,
&fndrInfo);
FSSpec applSpecTemp;
ProcessSerialNumber processSN;
err = FindProcessBySignature(fndrInfo.fdCreator, 'APPL', processSN, &applSpecTemp);
if (err == noErr) // App is running. Send 'odoc'
{
Try_ {
AppleEvent theEvent;
err = BuildODOCEvent(fndrInfo.fdCreator, theDocument, theEvent);
ThrowIfOSErr_(err);
AppleEvent result;
err = AESend(&theEvent, &result,
kAENoReply + kAEAlwaysInteract + kAECanSwitchLayer,
kAENormalPriority, kAEDefaultTimeout,
NULL,NULL);
AEDisposeDesc(&theEvent);
// err could be memFullErr, app is out of memory
ThrowIfOSErr_(err);
if (IsFrontApplication())
SetFrontProcess(&processSN);
}
Catch_(inErr) {
return inErr;
} EndCatch_
return noErr;
}
Try_ {
LaunchParamBlockRec launchThis;
AEDesc launchDesc;
AppleEvent theEvent;
ThrowIfOSErr_(BuildODOCEvent(fndrInfo.fdCreator, theDocument, theEvent));
ThrowIfOSErr_(AECoerceDesc(&theEvent, typeAppParameters, &launchDesc));
launchThis.launchAppSpec = (FSSpecPtr)&theApplication;
launchThis.launchAppParameters = (AppParametersPtr)*(launchDesc.dataHandle);
launchThis.launchBlockID = extendedBlock;
launchThis.launchEPBLength = extendedBlockLen;
launchThis.launchFileFlags = NULL;
launchThis.launchControlFlags = launchContinue + launchNoFileFlags + launchUseMinimum;
if (!IsFrontApplication())
launchThis.launchControlFlags += launchDontSwitch;
err = LaunchApplication(&launchThis);
ThrowIfOSErr_(err);
}
Catch_(inErr)
{
} EndCatch_
return err;
}
// Launches the application with the given doc
void LaunchWithDoc(FInfo& fndrInfo,
FSSpec& appSpec,
LFileBufferStream * inFile,
const FSSpec inFileSpec)
{
OSErr err = CFileMgr::FindApplication(fndrInfo.fdCreator, appSpec);
if (err) // Application not found error
{
int whatToDo = ::LaunchError(ALRT_AppNotFound, fndrInfo.fdCreator,
inFileSpec.name, err);
if (whatToDo == kSave)
CFileMgr::sFileManager.CancelRegister(inFile); // Save the file
else // kDelete
CFileMgr::sFileManager.CancelAndDelete(inFile); // Delete the file
return;
}
Try_ {
LaunchParamBlockRec launchThis;
AEDesc launchDesc;
AppleEvent theEvent;
ThrowIfOSErr_(BuildODOCEvent(fndrInfo.fdCreator, inFileSpec, theEvent));
ThrowIfOSErr_(AECoerceDesc(&theEvent, typeAppParameters, &launchDesc));
launchThis.launchAppSpec = (FSSpecPtr)&appSpec;
launchThis.launchAppParameters = (AppParametersPtr)*(launchDesc.dataHandle);
/* launch the thing */
launchThis.launchBlockID = extendedBlock;
launchThis.launchEPBLength = extendedBlockLen;
launchThis.launchFileFlags = NULL;
launchThis.launchControlFlags = launchContinue + launchNoFileFlags + launchUseMinimum;
if (!IsFrontApplication())
launchThis.launchControlFlags += launchDontSwitch;
do // Launch until we succeed, or user gives up.
{
err = LaunchApplication(&launchThis);
if ((err == memFullErr) || (err == memFragErr))
// Launch failed because of low memory
{
int whatToDo = ::LaunchError(ALRT_AppMemFull, fndrInfo.fdCreator,
inFileSpec.name, err);
switch (whatToDo) {
case kSave:
CFileMgr::sFileManager.CancelRegister(inFile); // Save the file
err = noErr;
break;
case kDelete:
CFileMgr::sFileManager.CancelAndDelete(inFile); // Save the file
err = noErr;
break;
case kTryAgain: // Loop again
break;
}
}
else // Unknown launch error
ThrowIfOSErr_(err);
} while (err != noErr);
}
Catch_(inErr)
{
int whatToDo = ::LaunchError(ALRT_AppMiscError, fndrInfo.fdCreator,
inFileSpec.name, inErr);
if (whatToDo == kSave)
CFileMgr::sFileManager.CancelRegister(inFile); // Save the file
else // kDelete
CFileMgr::sFileManager.CancelAndDelete(inFile); // Delete the file
} EndCatch_
}
OSErr
CreateFinderAppleEvent( AEEventID eventID,
SInt16 returnID,
SInt32 transactionID,
AppleEvent & theEvent)
{
OSErr err;
FSSpec finder;
ProcessSerialNumber psn;
AEDesc finderAddress;
Boolean validAddress = false;
try
{
err = FindProcessBySignature('MACS', 'FNDR', psn, &finder);
ThrowIfOSErr_(err);
err = ::AECreateDesc(typeProcessSerialNumber, (Ptr)&psn, sizeof(psn), &finderAddress);
ThrowIfOSErr_(err);
validAddress = true;
err = ::AECreateAppleEvent( kAEFinderEvents,
eventID,
(const AEAddressDesc *) &finderAddress,
returnID,
transactionID,
&theEvent );
}
catch(long tErr)
{
if (validAddress)
::AEDisposeDesc(&finderAddress);
}
return err;
}
// Builds an OpenSelection event for Finder
OSErr BuildOpenSelectionEvent(FSSpec & fileSpec, AppleEvent& theEvent) {
FSSpec dirSpec, procSpec;
FSSpecPtr theFileToOpen = nil;
CStr63 processName;
AEDesc aeDirDesc, listElem;
AEDesc fileList;
ConstStr255Param * dummy = NULL;
// Create the event
OSErr err;
Try_ {
ProcessInfoRec pir;
pir.processInfoLength = sizeof(ProcessInfoRec);
pir.processName = (StringPtr)&processName;
pir.processAppSpec = &procSpec;
// Find a Finder, and create its description as an address for an apple event
err = CreateFinderAppleEvent(kAEOpenSelection, kAutoGenerateReturnID, kAnyTransactionID, theEvent);
ThrowIfOSErr_(err);
// Create a description of the file, and the enclosing folder
// keyDirectObject is directory description
//
err = CFileMgr::FolderSpecFromFolderID(fileSpec.vRefNum, fileSpec.parID, dirSpec);
ThrowIfOSErr_(err);
err = AECreateList(nil, 0, false, &fileList);
ThrowIfOSErr_(err);
AliasHandle DirAlias, FileAlias;
NewAlias(nil, &dirSpec, &DirAlias);
HLock((Handle)DirAlias);
err = AECreateDesc(typeAlias, (Ptr)*DirAlias, GetHandleSize((Handle)DirAlias), &aeDirDesc);
ThrowIfOSErr_(err);
HUnlock((Handle)DirAlias);
DisposeHandle((Handle)DirAlias);
err = AEPutParamDesc(&theEvent, keyDirectObject, &aeDirDesc);
ThrowIfOSErr_(err);
AEDisposeDesc(&aeDirDesc);
NewAlias(nil, &fileSpec, &FileAlias);
HLock((Handle)FileAlias);
err = AECreateDesc(typeAlias, (Ptr)*FileAlias, GetHandleSize((Handle)FileAlias), &listElem);
ThrowIfOSErr_(err);
HUnlock((Handle)FileAlias);
err = AEPutDesc(&fileList, 0, &listElem);
ThrowIfOSErr_(err);
DisposeHandle((Handle)FileAlias);
err = AEPutParamDesc( &theEvent, keySelection, &fileList);
ThrowIfOSErr_(err);
}
Catch_(inErr)
{
return inErr;
} EndCatch_
return noErr;
}
// Sends 'open selection event to Finder
OSErr SendOpenSelectionToFinder(FSSpec & fileSpec)
{
AppleEvent event;
AppleEvent result;
OSErr err = BuildOpenSelectionEvent(fileSpec, event);
if (err)
return err;
err = AESend(&event, &result,
kAENoReply + kAEAlwaysInteract + kAECanSwitchLayer,
kAENormalPriority, kAEDefaultTimeout,
NULL,NULL);
AEDisposeDesc(&event);
return err;
}
// A somewhat tricky way of opening a foreign document
// Algorithm:
// - if a process is not running, launch it with the document
// - if a process is running and AE aware, send it an AppleEvent
// - if a process is running and is not AE aware, send openSelection to the Finder.
void LaunchFile(LFileBufferStream * inFile)
{
FSSpec applSpec;
FInfo fndrInfo;
// Get file info
FSSpec inFileSpec;
inFile->GetSpecifier(inFileSpec);
HGetFInfo(inFileSpec.vRefNum,
inFileSpec.parID,
inFileSpec.name,
&fndrInfo);
// Find if the application is already running
ProcessSerialNumber processSN;
ProcessInfoRec infoRecToFill;
Str63 processName;
infoRecToFill.processInfoLength = sizeof(ProcessInfoRec);
infoRecToFill.processName = (StringPtr)&processName;
infoRecToFill.processAppSpec = &applSpec;
OSErr err = FindProcessBySignature(fndrInfo.fdCreator, 'APPL', processSN, &applSpec);
if (err == noErr) // App is running. Send 'odoc'
{
err = SendODOCEvent(fndrInfo.fdCreator, inFile);
if (err == noErr)
{
if (IsFrontApplication())
SetFrontProcess(&processSN);
}
else
{
// Application did not accept apple event for some reason (err = connectionInvalid)
// Send 'odoc' to Finder. Finder can figure out how to fake menu events when
// it tries to open the file
err = SendOpenSelectionToFinder(inFileSpec);
if (err == noErr)
{ // If finder launched the application successfully, find it and bring it to front
err = FindProcessBySignature(fndrInfo.fdCreator, 'APPL', processSN, &applSpec);
if (err == noErr && IsFrontApplication())
SetFrontProcess(&processSN);
}
else // Finder launch also failed. Notify the user
{
//Notify the user, try to handle the error
int whatToDo = LaunchError(ALRT_ODOCFailed,
fndrInfo.fdCreator,
inFileSpec.name, err);
if (whatToDo == 1)
CFileMgr::sFileManager.CancelRegister(inFile); // Save the file
else
CFileMgr::sFileManager.CancelAndDelete(inFile); // Delete the file
}
}
}
else // App is not running. Launch it with this file
LaunchWithDoc(fndrInfo, applSpec, inFile, inFileSpec);
}