git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@4950 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
470 lines
12 KiB
C++
470 lines
12 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
||
// 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"
|
||
|
||
IMPLEMENT_CLASS(wxDirDialog, wxDialog)
|
||
|
||
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
|
||
};
|
||
|
||
// 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
|
||
|
||
struct UserDataRec {
|
||
StandardFileReply *sfrPtr;
|
||
FSSpec oldSelectionFSSpec;
|
||
DialogPtr theDlgPtr;
|
||
};
|
||
typedef struct UserDataRec
|
||
UserDataRec, *UserDataRecPtr;
|
||
|
||
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, Ptr 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, Ptr 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
|
||
|
||
CopyPStr("\p ", theSFR->sfFile.name);
|
||
|
||
// point the user data parameter at the reply record so we can get to it later
|
||
|
||
myData.sfrPtr = theSFR;
|
||
|
||
// display the dialog
|
||
|
||
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);
|
||
|
||
// 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, Ptr 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);
|
||
}
|
||
|
||
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()
|
||
{
|
||
{
|
||
Str255 prompt ;
|
||
Str255 path ;
|
||
|
||
strcpy((char *)prompt, m_message) ;
|
||
c2pstr((char *)prompt ) ;
|
||
|
||
strcpy((char *)path, m_path ) ;
|
||
c2pstr((char *)path ) ;
|
||
|
||
FileFilterYDUPP invisiblesExcludedCustomFilterUPP;
|
||
StandardFileReply reply ;
|
||
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;
|
||
}
|
||
|