Files
wxWidgets/src/mac/dirdlg.cpp
Gilles Depeyrot 5fde6fcc9b corrections for final release of Mac OS X
applied patches proposed by Mark Newsam


git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@9625 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2001-04-01 20:42:05 +00:00

600 lines
15 KiB
C++
Raw Blame History

/////////////////////////////////////////////////////////////////////////////
// Name: dirdlg.cpp
// Purpose: wxDirDialog
// Author: AUTHOR
// Modified by:
// Created: ??/??/98
// RCS-ID: $Id$
// Copyright: (c) AUTHOR
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
#ifdef __GNUG__
#pragma implementation "dirdlg.h"
#endif
#include "wx/defs.h"
#include "wx/utils.h"
#include "wx/dialog.h"
#include "wx/dirdlg.h"
#include "wx/cmndata.h"
#if defined(__UNIX__)
#include <Carbon/Carbon.h>
#else
#include <Navigation.h>
#endif
#if !USE_SHARED_LIBRARY
IMPLEMENT_CLASS(wxDirDialog, wxDialog)
#endif
bool gUseNavServices = NavServicesAvailable() ;
// the data we need to pass to our standard file hook routine
// includes a pointer to the dialog, a pointer to the standard
// file reply record (so we can inspect the current selection)
// and a copy of the "previous" file spec of the reply record
// so we can see if the selection has changed
#if !TARGET_CARBON
struct UserDataRec {
StandardFileReply *sfrPtr;
FSSpec oldSelectionFSSpec;
DialogPtr theDlgPtr;
};
typedef struct UserDataRec
UserDataRec, *UserDataRecPtr;
enum {
kSelectItem = 10, // select button item number
kSFGetFolderDlgID = 250, // dialog resource number
kStrListID = 250, // our strings
kSelectStrNum = 1, // word 'Select: ' for button
kDesktopStrNum = 2, // word 'Desktop' for button
kSelectNoQuoteStrNum = 3, // word 'Select: ' for button
kUseQuotes = true, // parameter for SetButtonName
kDontUseQuotes = false
};
static void GetLabelString(StringPtr theStr, short stringNum)
{
GetIndString(theStr, kStrListID, stringNum);
}
static void CopyPStr(StringPtr src, StringPtr dest)
{
BlockMoveData(src, dest, 1 + src[0]);
}
static char GetSelectKey(void)
{
// this is the key used to trigger the select button
// NOT INTERNATIONAL SAVVY; should at least grab it from resources
return 's';
}
// SetButtonName sets the name of the Select button in the dialog
//
// To do this, we need to call the Script Manager to truncate the
// label in the middle to fit the button and to merge the button
// name with the word Select (possibly followed by quotes). Using
// the Script Manager avoids all sorts of problems internationally.
//
// buttonName is the name to appear following the word Select
// quoteFlag should be true if the name is to appear in quotes
static void SetButtonName(DialogPtr theDlgPtr, short buttonID, StringPtr buttonName,
Boolean quoteFlag)
{
short buttonType;
Handle buttonHandle;
Rect buttonRect;
short textWidth;
Handle labelHandle;
Handle nameHandle;
Str15 keyStr;
Str255 labelStr;
OSErr err;
nameHandle = nil;
labelHandle = nil;
// get the details of the button from the dialog
GetDialogItem(theDlgPtr, buttonID, &buttonType, &buttonHandle, &buttonRect);
// get the string for the select button label, "Select ^0" or "Select <20>^0<>"
GetLabelString(labelStr, (quoteFlag == kUseQuotes) ? kSelectStrNum : kSelectNoQuoteStrNum);
// make string handles containing the select button label and the
// file name to be stuffed into the button
err = PtrToHand(&labelStr[1], &labelHandle, labelStr[0]);
if (err != noErr) goto Bail;
// cut out the middle of the file name to fit the button
//
// we'll temporarily use labelStr here to hold the modified button name
// since we don't own the buttonName string storage space
textWidth = (buttonRect.right - buttonRect.left) - StringWidth(labelStr);
CopyPStr(buttonName, labelStr);
(void) TruncString(textWidth, labelStr, smTruncMiddle);
err = PtrToHand(&labelStr[1], &nameHandle, labelStr[0]);
if (err != noErr) goto Bail;
// replace the ^0 in the Select string with the file name
CopyPStr("\p^0", keyStr);
(void) ReplaceText(labelHandle, nameHandle, keyStr);
labelStr[0] = (unsigned char) GetHandleSize(labelHandle);
BlockMoveData(*labelHandle, &labelStr[1], labelStr[0]);
// now set the control title, and re-validate the area
// above the control to avoid a needless redraw
SetControlTitle((ControlHandle) buttonHandle, labelStr);
ValidRect(&buttonRect);
Bail:
if (nameHandle) DisposeHandle(nameHandle);
if (labelHandle) DisposeHandle(labelHandle);
}
// FlashButton briefly highlights the dialog button
// as feedback for key equivalents
static void FlashButton(DialogPtr theDlgPtr, short buttonID)
{
short buttonType;
Handle buttonHandle;
Rect buttonRect;
unsigned long finalTicks;
GetDialogItem(theDlgPtr, buttonID, &buttonType, &buttonHandle, &buttonRect);
HiliteControl((ControlHandle) buttonHandle, kControlButtonPart);
Delay(10, &finalTicks);
HiliteControl((ControlHandle) buttonHandle, 0);
}
static Boolean SameFSSpec(FSSpecPtr spec1, FSSpecPtr spec2)
{
return (spec1->vRefNum == spec2->vRefNum
&& spec1->parID == spec2->parID
&& EqualString(spec1->name, spec2->name, false, false));
}
// MyModalDialogFilter maps a key to the Select button, and handles
// flashing of the button when the key is hit
static pascal Boolean SFGetFolderModalDialogFilter(DialogPtr theDlgPtr, EventRecord *eventRec,
short *item, void *dataPtr)
{
#pragma unused (dataPtr)
// make certain the proper dialog is showing, 'cause standard file
// can nest dialogs but calls the same filter for each
if (((WindowPeek) theDlgPtr)->refCon == sfMainDialogRefCon)
{
// check if the select button was hit
if ((eventRec->what == keyDown)
&& (eventRec->modifiers & cmdKey)
&& ((eventRec->message & charCodeMask) == GetSelectKey()))
{
*item = kSelectItem;
FlashButton(theDlgPtr, kSelectItem);
return true;
}
}
return false;
}
// MyDlgHook is a hook routine that maps the select button to Open
// and sets the Select button name
static pascal short SFGetFolderDialogHook(short item, DialogPtr theDlgPtr, void *dataPtr)
{
UserDataRecPtr theUserDataRecPtr;
long desktopDirID;
short desktopVRefNum;
FSSpec tempSpec;
Str63 desktopName;
OSErr err;
// be sure Std File is really showing us the intended dialog,
// not a nested modal dialog
if (((WindowPeek) theDlgPtr)->refCon != sfMainDialogRefCon)
{
return item;
}
theUserDataRecPtr = (UserDataRecPtr) dataPtr;
// map the Select button to Open
if (item == kSelectItem)
{
item = sfItemOpenButton;
}
// find the desktop folder
err = FindFolder(theUserDataRecPtr->sfrPtr->sfFile.vRefNum,
kDesktopFolderType, kDontCreateFolder,
&desktopVRefNum, &desktopDirID);
if (err != noErr)
{
// for errors, get value that won't match any real vRefNum/dirID
desktopVRefNum = 0;
desktopDirID = 0;
}
// change the Select button label if the selection has changed or
// if this is the first call to the hook
if (item == sfHookFirstCall
|| item == sfHookChangeSelection
|| item == sfHookRebuildList
|| ! SameFSSpec(&theUserDataRecPtr->sfrPtr->sfFile,
&theUserDataRecPtr->oldSelectionFSSpec))
{
// be sure there is a file name selected
if (theUserDataRecPtr->sfrPtr->sfFile.name[0] != '\0')
{
SetButtonName(theDlgPtr, kSelectItem,
theUserDataRecPtr->sfrPtr->sfFile.name,
kUseQuotes); // true -> use quotes
}
else
{
// is the desktop selected?
if (theUserDataRecPtr->sfrPtr->sfFile.vRefNum == desktopVRefNum
&& theUserDataRecPtr->sfrPtr->sfFile.parID == desktopDirID)
{
// set button to "Select Desktop"
GetLabelString(desktopName, kDesktopStrNum);
SetButtonName(theDlgPtr, kSelectItem,
desktopName, kDontUseQuotes); // false -> no quotes
}
else
{
// get parent directory's name for the Select button
//
// passing an empty name string to FSMakeFSSpec gets the
// name of the folder specified by the parID parameter
(void) FSMakeFSSpec(theUserDataRecPtr->sfrPtr->sfFile.vRefNum,
theUserDataRecPtr->sfrPtr->sfFile.parID, "\p",
&tempSpec);
SetButtonName(theDlgPtr, kSelectItem,
tempSpec.name, kUseQuotes); // true -> use quotes
}
}
}
// save the current selection as the old selection for comparison next time
//
// it's not valid on the first call, though, or if we don't have a
// name available from standard file
if (item != sfHookFirstCall || theUserDataRecPtr->sfrPtr->sfFile.name[0] != '\0')
{
theUserDataRecPtr->oldSelectionFSSpec = theUserDataRecPtr->sfrPtr->sfFile;
}
else
{
// on first call, empty string won't set the button correctly,
// so invalidate oldSelection
theUserDataRecPtr->oldSelectionFSSpec.vRefNum = 999;
theUserDataRecPtr->oldSelectionFSSpec.parID = 0;
}
return item;
}
void StandardGetFolder( ConstStr255Param message , ConstStr255Param path , FileFilterYDUPP fileFilter, StandardFileReply *theSFR)
{
Point thePt;
SFTypeList mySFTypeList;
UserDataRec myData;
FSSpec tempSpec;
Boolean folderFlag;
Boolean wasAliasedFlag;
DlgHookYDUPP dlgHookUPP;
ModalFilterYDUPP myModalFilterUPP;
OSErr err;
// presumably we're running System 7 or later so CustomGetFile is
// available
// set initial contents of Select button to a space
memcpy(theSFR->sfFile.name, "\p ", 2);
// point the user data parameter at the reply record so we can get to it later
myData.sfrPtr = theSFR;
// display the dialog
#if !TARGET_CARBON
dlgHookUPP = NewDlgHookYDProc(SFGetFolderDialogHook);
myModalFilterUPP = NewModalFilterYDProc(SFGetFolderModalDialogFilter);
thePt.h = thePt.v = -1; // center dialog
ParamText( message , NULL , NULL , NULL ) ;
CustomGetFile( fileFilter,
-1, // show all types
mySFTypeList,
theSFR,
kSFGetFolderDlgID,
thePt, // top left point
dlgHookUPP,
myModalFilterUPP,
nil, // activate list
nil, // activate proc
&myData);
DisposeRoutineDescriptor(dlgHookUPP);
DisposeRoutineDescriptor(myModalFilterUPP);
#else
#endif
// if cancel wasn't pressed and no fatal error occurred...
if (theSFR->sfGood)
{
// if no name is in the reply record file spec,
// use the file spec of the parent folder
if (theSFR->sfFile.name[0] == '\0')
{
err = FSMakeFSSpec(theSFR->sfFile.vRefNum, theSFR->sfFile.parID,
"\p", &tempSpec);
if (err == noErr)
{
theSFR->sfFile = tempSpec;
}
else
{
// no name to return, forget it
theSFR->sfGood = false;
}
}
// if there is now a name in the file spec, check if it's
// for a folder or a volume
if (theSFR->sfFile.name[0] != '\0')
{
// the parID of the root of a disk is always fsRtParID == 1
if (theSFR->sfFile.parID == fsRtParID)
{
theSFR->sfIsVolume = true;
theSFR->sfIsFolder = false; // it would be reasonable for this to be true, too
}
// we have a valid FSSpec, now let's make sure it's not for an alias file
err = ResolveAliasFile(&theSFR->sfFile, true, &folderFlag, &wasAliasedFlag);
if (err != noErr)
{
theSFR->sfGood = false;
}
// did the alias resolve to a folder?
if (folderFlag && ! theSFR->sfIsVolume)
{
theSFR->sfIsFolder = true;
}
}
}
}
static pascal Boolean OnlyVisibleFoldersCustomFileFilter(CInfoPBPtr myCInfoPBPtr, void *dataPtr)
{
#pragma unused (dataPtr)
// return true if this item is invisible or a file
Boolean visibleFlag;
Boolean folderFlag;
visibleFlag = ! (myCInfoPBPtr->hFileInfo.ioFlFndrInfo.fdFlags & kIsInvisible);
folderFlag = (myCInfoPBPtr->hFileInfo.ioFlAttrib & 0x10);
// because the semantics of the filter proc are "true means don't show
// it" we need to invert the result that we return
return !(visibleFlag && folderFlag);
}
#endif
wxDirDialog::wxDirDialog(wxWindow *parent, const wxString& message,
const wxString& defaultPath,
long style, const wxPoint& pos)
{
m_message = message;
m_dialogStyle = style;
m_parent = parent;
m_path = defaultPath;
}
int wxDirDialog::ShowModal()
{
#if !TARGET_CARBON
if ( !gUseNavServices )
{
Str255 prompt ;
Str255 path ;
#if TARGET_CARBON
c2pstrcpy((StringPtr)prompt, m_message) ;
#else
strcpy((char *)prompt, m_message) ;
c2pstr((char *)prompt ) ;
#endif
#if TARGET_CARBON
c2pstrcpy((StringPtr)path, m_path ) ;
#else
strcpy((char *)path, m_path ) ;
c2pstr((char *)path ) ;
#endif
StandardFileReply reply ;
FileFilterYDUPP invisiblesExcludedCustomFilterUPP = 0 ;
invisiblesExcludedCustomFilterUPP =
NewFileFilterYDProc(OnlyVisibleFoldersCustomFileFilter);
StandardGetFolder( prompt , path , invisiblesExcludedCustomFilterUPP, &reply);
DisposeRoutineDescriptor(invisiblesExcludedCustomFilterUPP);
if ( reply.sfGood == false )
{
m_path = "" ;
return wxID_CANCEL ;
}
else
{
m_path = wxMacFSSpec2UnixFilename( &reply.sfFile ) ;
return wxID_OK ;
}
return wxID_CANCEL;
}
else
#endif
{
NavDialogOptions mNavOptions;
NavObjectFilterUPP mNavFilterUPP = NULL;
NavPreviewUPP mNavPreviewUPP = NULL ;
NavReplyRecord mNavReply;
AEDesc* mDefaultLocation = NULL ;
bool mSelectDefault = false ;
::NavGetDefaultDialogOptions(&mNavOptions);
mNavFilterUPP = nil;
mNavPreviewUPP = nil;
mSelectDefault = false;
mNavReply.validRecord = false;
mNavReply.replacing = false;
mNavReply.isStationery = false;
mNavReply.translationNeeded = false;
mNavReply.selection.descriptorType = typeNull;
mNavReply.selection.dataHandle = nil;
mNavReply.keyScript = smSystemScript;
mNavReply.fileTranslation = nil;
// Set default location, the location
// that's displayed when the dialog
// first appears
if ( mDefaultLocation ) {
if (mSelectDefault) {
mNavOptions.dialogOptionFlags |= kNavSelectDefaultLocation;
} else {
mNavOptions.dialogOptionFlags &= ~kNavSelectDefaultLocation;
}
}
OSErr err = ::NavChooseFolder(
mDefaultLocation,
&mNavReply,
&mNavOptions,
NULL,
mNavFilterUPP,
0L); // User Data
if ( (err != noErr) && (err != userCanceledErr) ) {
m_path = "" ;
return wxID_CANCEL ;
}
if (mNavReply.validRecord) { // User chose a folder
FSSpec folderInfo;
FSSpec outFileSpec ;
AEDesc specDesc ;
OSErr err = ::AECoerceDesc( &mNavReply.selection , typeFSS, &specDesc);
if ( err != noErr ) {
m_path = "" ;
return wxID_CANCEL ;
}
folderInfo = **(FSSpec**) specDesc.dataHandle;
if (specDesc.dataHandle != nil) {
::AEDisposeDesc(&specDesc);
}
// mNavReply.GetFileSpec(folderInfo);
// The FSSpec from NavChooseFolder is NOT the file spec
// for the folder. The parID field is actually the DirID
// of the folder itself, not the folder's parent, and
// the name field is empty. We must call PBGetCatInfo
// to get the parent DirID and folder name
Str255 name;
CInfoPBRec thePB; // Directory Info Parameter Block
thePB.dirInfo.ioCompletion = nil;
thePB.dirInfo.ioVRefNum = folderInfo.vRefNum; // Volume is right
thePB.dirInfo.ioDrDirID = folderInfo.parID; // Folder's DirID
thePB.dirInfo.ioNamePtr = name;
thePB.dirInfo.ioFDirIndex = -1; // Lookup using Volume and DirID
err = ::PBGetCatInfoSync(&thePB);
if ( err != noErr ) {
m_path = "" ;
return wxID_CANCEL ;
}
// Create cannonical FSSpec
::FSMakeFSSpec(thePB.dirInfo.ioVRefNum, thePB.dirInfo.ioDrParID,
name, &outFileSpec);
// outFolderDirID = thePB.dirInfo.ioDrDirID;
m_path = wxMacFSSpec2UnixFilename( &outFileSpec ) ;
return wxID_OK ;
}
return wxID_CANCEL;
}
}