summaryrefslogtreecommitdiffstats
path: root/macosx/tkMacOSXDialog.c
diff options
context:
space:
mode:
authorwolfsuit <wolfsuit@noemail.net>2004-02-16 00:42:32 (GMT)
committerwolfsuit <wolfsuit@noemail.net>2004-02-16 00:42:32 (GMT)
commitec54861ba969c9a343d9fc7aa79477889dad1cb8 (patch)
tree12bebf3e6d48b35ab147bf17d2c87861606f8460 /macosx/tkMacOSXDialog.c
parent4a8260dea385cb403a930ccd19bd3eb532495dfa (diff)
downloadtk-ec54861ba969c9a343d9fc7aa79477889dad1cb8.zip
tk-ec54861ba969c9a343d9fc7aa79477889dad1cb8.tar.gz
tk-ec54861ba969c9a343d9fc7aa79477889dad1cb8.tar.bz2
Backport Mac OS X specific fixes from TOT. See ChangeLog for details.
In tk_getOpenFile filters, handle the case where you have a Macintosh file type and the files have no filetype. Fix various formatting nits. FossilOrigin-Name: 834854dbdc52160c4050e54c91e1ffa91c9efbb0
Diffstat (limited to 'macosx/tkMacOSXDialog.c')
-rw-r--r--macosx/tkMacOSXDialog.c498
1 files changed, 465 insertions, 33 deletions
diff --git a/macosx/tkMacOSXDialog.c b/macosx/tkMacOSXDialog.c
index 25883ce..074dca6 100644
--- a/macosx/tkMacOSXDialog.c
+++ b/macosx/tkMacOSXDialog.c
@@ -9,7 +9,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tkMacOSXDialog.c,v 1.4.2.1 2004/02/14 01:08:28 wolfsuit Exp $
+ * RCS: @(#) $Id: tkMacOSXDialog.c,v 1.4.2.2 2004/02/16 00:42:34 wolfsuit Exp $
*/
#include <Carbon/Carbon.h>
@@ -58,6 +58,18 @@ typedef struct _OpenFileData {
} OpenFileData;
+/*
+ * The following structure is used in the tk_messageBox
+ * implementation.
+ */
+typedef struct {
+ WindowRef windowRef;
+ int buttonIndex;
+} CallbackUserData;
+
+
+static OSStatus AlertHandler _ANSI_ARGS_(( EventHandlerCallRef callRef,
+ EventRef eventRef, void *userData ));
static Boolean MatchOneType _ANSI_ARGS_((StringPtr fileNamePtr, OSType fileType,
OpenFileData *myofdPtr, FileFilter *filterPtr));
static pascal Boolean OpenFileFilterProc(AEDesc* theItem, void* info,
@@ -747,11 +759,11 @@ NavServicesGetFile(
diagOptions.location.v = -1;
diagOptions.optionFlags = kNavDontAutoTranslate
+ kNavDontAddTranslateItems;
-
if (multiple) {
diagOptions.optionFlags += kNavAllowMultipleFiles;
}
-
+ diagOptions.modality = kWindowModalityAppModal;
+
if (ofdPtr != NULL && ofdPtr->usePopup) {
FileFilter *filterPtr;
@@ -775,7 +787,7 @@ NavServicesGetFile(
filterPtr->name, encoding);
}
diagOptions.popupExtension = CFArrayCreate(NULL,
- (const void **)menuItemNames, ofdPtr->fl.numFilters, NULL);;
+ (const void **) menuItemNames, ofdPtr->fl.numFilters, NULL);
} else {
diagOptions.optionFlags += kNavNoTypePopup;
diagOptions.popupExtension = NULL;
@@ -815,7 +827,7 @@ NavServicesGetFile(
openFileFilterUPP,
ofdPtr,
&dialogRef);
- if (err!=noErr){
+ if (err != noErr) {
fprintf(stderr,"NavCreateGetFileDialog failed, %d\n", err );
dialogRef = NULL;
}
@@ -857,7 +869,7 @@ NavServicesGetFile(
* Most commands assume that the file dialogs return a single
* item, not a list. So only build a list if multiple is true...
*/
- if (err==noErr) {
+ if (err == noErr) {
if (multiple) {
theResult = Tcl_NewListObj(0, NULL);
} else {
@@ -867,7 +879,7 @@ NavServicesGetFile(
err = memFullErr;
}
}
- if (theReply.validRecord && err==noErr) {
+ if (theReply.validRecord && err == noErr) {
AEDesc resultDesc;
long count;
Tcl_DString fileName;
@@ -962,7 +974,7 @@ NavServicesGetFile(
if (menuItemNames) {
int i;
- for (i=0;i < ofdPtr->fl.numFilters;i++) {
+ for (i = 0;i < ofdPtr->fl.numFilters; i++) {
CFRelease(menuItemNames[i]);
}
ckfree((void *)menuItemNames);
@@ -981,30 +993,59 @@ OpenFileFilterProc(
NavFilterModes filterMode )
{
OpenFileData *ofdPtr = (OpenFileData *) callBackUD;
+
if (!ofdPtr || !ofdPtr->usePopup) {
return true;
} else {
if (ofdPtr->fl.numFilters == 0) {
return true;
} else {
-
- if ( theItem->descriptorType == typeFSS ) {
- NavFileOrFolderInfo* theInfo = (NavFileOrFolderInfo*)info;
+ if ((theItem->descriptorType == typeFSS)
+ || (theItem->descriptorType = typeFSRef)) {
+ NavFileOrFolderInfo* theInfo = (NavFileOrFolderInfo *) info;
+ char fileName[256];
int result;
if ( !theInfo->isFolder ) {
OSType fileType;
StringPtr fileNamePtr;
+ Tcl_DString fileNameDString;
int i;
FileFilter *filterPtr;
- fileType = theInfo->fileAndFolder.fileInfo.finderInfo.fdType;
- HLock((Handle)theItem->dataHandle);
- fileNamePtr = (((FSSpec *) *theItem->dataHandle)->name);
+ fileType =
+ theInfo->fileAndFolder.fileInfo.finderInfo.fdType;
+ Tcl_DStringInit (&fileNameDString);
+ if (theItem->descriptorType == typeFSS) {
+ int len;
+ fileNamePtr = (((FSSpec *) *theItem->dataHandle)->name);
+ len = fileNamePtr[0];
+ strncpy(fileName, fileNamePtr + 1, len);
+ fileName[len] = '\0';
+ fileNamePtr = fileName;
+
+ } else if (theItem->descriptorType = typeFSRef) {
+ OSStatus err;
+ FSRef *theRef = (FSRef *) *theItem->dataHandle;
+ HFSUniStr255 uniFileName;
+ err = FSGetCatalogInfo (theRef, kFSCatInfoNone, NULL,
+ &uniFileName, NULL, NULL);
+
+ if (err == noErr) {
+ Tcl_UniCharToUtfDString (
+ (Tcl_UniChar *) uniFileName.unicode,
+ uniFileName.length,
+ &fileNameDString);
+ fileNamePtr = Tcl_DStringValue(&fileNameDString);
+ } else {
+ fileNamePtr = NULL;
+ }
+ }
if (ofdPtr->usePopup) {
i = ofdPtr->curType;
- for (filterPtr = ofdPtr->fl.filters; filterPtr && i > 0; i--) {
+ for (filterPtr = ofdPtr->fl.filters;
+ filterPtr && i > 0; i--) {
filterPtr = filterPtr->next;
}
if (filterPtr) {
@@ -1015,9 +1056,11 @@ OpenFileFilterProc(
}
} else {
/*
- * We are not using the popup menu. In this case, the file is
- * considered matched if it matches any of the file filters.
+ * We are not using the popup menu. In this case, the
+ * file is considered matched if it matches any of
+ * the file filters.
*/
+
result = UNMATCHED;
for (filterPtr = ofdPtr->fl.filters; filterPtr;
filterPtr = filterPtr->next) {
@@ -1029,7 +1072,7 @@ OpenFileFilterProc(
}
}
- HUnlock((Handle)theItem->dataHandle);
+ Tcl_DStringFree (&fileNameDString);
return (result == MATCHED);
} else {
return true;
@@ -1086,7 +1129,7 @@ OpenEventProc(
static Boolean
MatchOneType(
StringPtr fileNamePtr, /* Name of the file */
- OSType fileType, /* Type of the file */
+ OSType fileType, /* Type of the file, 0 means there was no specified type. */
OpenFileData * ofdPtr, /* Information about this file dialog */
FileFilter * filterPtr) /* Match the file described by pb against
* this filter */
@@ -1110,7 +1153,8 @@ MatchOneType(
* considered an error.
*/
- for (clausePtr=filterPtr->clauses; clausePtr; clausePtr=clausePtr->next) {
+ for (clausePtr = filterPtr->clauses; clausePtr;
+ clausePtr = clausePtr->next) {
int macMatched = 0;
int globMatched = 0;
GlobPattern * globPtr;
@@ -1123,18 +1167,13 @@ MatchOneType(
macMatched = 1;
}
- for (globPtr=clausePtr->patterns; globPtr; globPtr=globPtr->next) {
- char filename[256];
- int len;
- char * p, *q, *ext;
+ for (globPtr = clausePtr->patterns; globPtr;
+ globPtr = globPtr->next) {
+ char *q, *ext;
if (fileNamePtr == NULL) {
continue;
}
- p = (char*)(fileNamePtr);
- len = p[0];
- strncpy(filename, p+1, len);
- filename[len] = '\0';
ext = globPtr->pattern;
if (ext[0] == '\0') {
@@ -1142,7 +1181,8 @@ MatchOneType(
* We don't want any extensions: OK if the filename doesn't
* have "." in it
*/
- for (q=filename; *q; q++) {
+
+ for (q = fileNamePtr; *q; q++) {
if (*q == '.') {
goto glob_unmatched;
}
@@ -1150,7 +1190,7 @@ MatchOneType(
goto glob_matched;
}
- if (Tcl_StringMatch(filename, ext)) {
+ if (Tcl_StringMatch(fileNamePtr, ext)) {
goto glob_matched;
} else {
goto glob_unmatched;
@@ -1164,14 +1204,24 @@ MatchOneType(
break;
}
- for (mfPtr=clausePtr->macTypes; mfPtr; mfPtr=mfPtr->next) {
+ for (mfPtr = clausePtr->macTypes; mfPtr; mfPtr = mfPtr->next) {
if (fileType == mfPtr->type) {
macMatched = 1;
break;
}
}
- if (globMatched && macMatched) {
+ /*
+ * On Mac OS X, it is not uncommon for files to have NO
+ * file type. But folks with Tcl code on Classic MacOS pretty
+ * much assume that a generic file will have type TEXT. So
+ * if we were strict about matching types when the source file
+ * had NO type set, they would have to add another rule always
+ * with no fileType. To avoid that, we pass the macMatch side
+ * of the test if no fileType is set.
+ */
+
+ if (globMatched && (macMatched || (fileType == 0))) {
return MATCHED;
}
}
@@ -1209,7 +1259,7 @@ TkAboutDlg()
return;
}
- windowRef=GetDialogWindow(aboutDlog);
+ windowRef = GetDialogWindow(aboutDlog);
SelectWindow(windowRef);
while (itemHit != 1) {
@@ -1222,3 +1272,385 @@ TkAboutDlg()
return;
}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_MessageBoxObjCmd --
+ *
+ * Implements the tk_messageBox in native Mac OS X style.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * none
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tk_MessageBoxObjCmd(
+ ClientData clientData, /* Main window associated with interpreter. */
+ Tcl_Interp *interp, /* Current interpreter. */
+ int objc, /* Number of arguments. */
+ Tcl_Obj *CONST objv[]) /* Argument objects. */
+{
+ Tk_Window tkwin = (Tk_Window) clientData;
+ AlertStdCFStringAlertParamRec paramCFStringRec;
+ AlertType alertType;
+ DialogRef dialogRef;
+ CFStringRef messageTextCF = NULL;
+ CFStringRef finemessageTextCF = NULL;
+ OSErr osError;
+ SInt16 itemHit;
+ Boolean haveDefaultOption = false;
+ Boolean haveParentOption = false;
+ char *str;
+ int index;
+ int defaultButtonIndex;
+ int defaultNativeButtonIndex; /* 1, 2, 3: right to left. */
+ int typeIndex;
+ int i;
+ int indexDefaultOption;
+ int result = TCL_OK;
+
+ static CONST char *movableAlertStrings[] = {
+ "-default", /* "-finemessage", */ "-icon",
+ "-message", "-parent",
+ "-title", "-type",
+ (char *)NULL
+ };
+ static CONST char *movableTypeStrings[] = {
+ "abortretryignore", "ok",
+ "okcancel", "retrycancel",
+ "yesno", "yesnocancel",
+ (char *)NULL
+ };
+ static CONST char *movableButtonStrings[] = {
+ "abort", "retry", "ignore",
+ "ok", "cancel", "yes", "no",
+ (char *)NULL
+ };
+ static CONST char *movableIconStrings[] = {
+ "error", "info", "question", "warning",
+ (char *)NULL
+ };
+ enum movableAlertOptions {
+ ALERT_DEFAULT, /* ALERT_FINEMESSAGE, */ ALERT_ICON,
+ ALERT_MESSAGE, ALERT_PARENT,
+ ALERT_TITLE, ALERT_TYPE
+ };
+ enum movableTypeOptions {
+ TYPE_ABORTRETRYIGNORE, TYPE_OK,
+ TYPE_OKCANCEL, TYPE_RETRYCANCEL,
+ TYPE_YESNO, TYPE_YESNOCANCEL
+ };
+ enum movableButtonOptions {
+ TEXT_ABORT, TEXT_RETRY, TEXT_IGNORE,
+ TEXT_OK, TEXT_CANCEL, TEXT_YES, TEXT_NO
+ };
+ enum movableIconOptions {
+ ICON_ERROR, ICON_INFO, ICON_QUESTION, ICON_WARNING
+ };
+
+ /*
+ * Need to map from 'movableButtonStrings' and its corresponding integer index,
+ * to the native button index, which is 1, 2, 3, from right to left.
+ * This is necessary to do for each separate '-type' of button sets.
+ */
+
+ short buttonIndexAndTypeToNativeButtonIndex[][7] = {
+ /* abort retry ignore ok cancel yes no */
+ {1, 2, 3, 0, 0, 0, 0}, /* abortretryignore */
+ {0, 0, 0, 1, 0, 0, 0}, /* ok */
+ {0, 0, 0, 1, 2, 0, 0}, /* okcancel */
+ {0, 1, 0, 0, 2, 0, 0}, /* retrycancel */
+ {0, 0, 0, 0, 0, 1, 2}, /* yesno */
+ {0, 0, 0, 0, 3, 1, 2}, /* yesnocancel */
+ };
+
+ /*
+ * Need also the inverse mapping, from native button (1, 2, 3) to the
+ * descriptive button text string index.
+ */
+
+ short nativeButtonIndexAndTypeToButtonIndex[][4] = {
+ {-1, 0, 1, 2}, /* abortretryignore */
+ {-1, 3, 0, 0}, /* ok */
+ {-1, 3, 4, 0}, /* okcancel */
+ {-1, 1, 4, 0}, /* retrycancel */
+ {-1, 5, 6, 0}, /* yesno */
+ {-1, 5, 6, 4}, /* yesnocancel */
+ };
+
+ alertType = kAlertPlainAlert;
+ typeIndex = TYPE_OK;
+
+ GetStandardAlertDefaultParams( &paramCFStringRec, kStdCFStringAlertVersionOne );
+ paramCFStringRec.movable = true;
+ paramCFStringRec.helpButton = false;
+ paramCFStringRec.defaultButton = kAlertStdAlertOKButton;
+
+ for (i = 1; i < objc; i += 2) {
+ int iconIndex;
+ char *string;
+
+ if (Tcl_GetIndexFromObj( interp, objv[i], movableAlertStrings, "option",
+ TCL_EXACT, &index ) != TCL_OK) {
+ result = TCL_ERROR;
+ goto end;
+ }
+ if (i + 1 == objc) {
+ string = Tcl_GetStringFromObj( objv[i], NULL );
+ Tcl_AppendResult(interp, "value for \"", string, "\" missing",
+ (char *) NULL);
+ result = TCL_ERROR;
+ goto end;
+ }
+
+ switch (index) {
+
+ case ALERT_DEFAULT:
+
+ /*
+ * Need to postpone processing of this option until we are
+ * sure to know the '-type' as well.
+ */
+
+ haveDefaultOption = true;
+ indexDefaultOption = i;
+ break;
+
+/* case ALERT_FINEMESSAGE:
+ str = Tcl_GetStringFromObj( objv[i + 1], NULL );
+ finemessageTextCF = CFStringCreateWithCString( NULL, str, kCFStringEncodingUTF8 );
+ break;
+*/
+ case ALERT_ICON:
+ /* not sure about UTF translation here... */
+ if (Tcl_GetIndexFromObj( interp, objv[i + 1], movableIconStrings,
+ "value", TCL_EXACT, &iconIndex ) != TCL_OK) {
+ result = TCL_ERROR;
+ goto end;
+ }
+ switch (iconIndex) {
+ case ICON_ERROR:
+ alertType = kAlertStopAlert;
+ break;
+ case ICON_INFO:
+ alertType = kAlertNoteAlert;
+ break;
+ case ICON_QUESTION:
+ alertType = kAlertCautionAlert;
+ break;
+ case ICON_WARNING:
+ alertType = kAlertCautionAlert;
+ break;
+ }
+ break;
+
+ case ALERT_MESSAGE:
+ str = Tcl_GetStringFromObj( objv[i + 1], NULL );
+ messageTextCF = CFStringCreateWithCString( NULL, str, kCFStringEncodingUTF8 );
+ break;
+
+ case ALERT_PARENT:
+ str = Tcl_GetStringFromObj( objv[i + 1], NULL );
+ tkwin = Tk_NameToWindow( interp, str, tkwin );
+ if (tkwin == NULL) {
+ result = TCL_ERROR;
+ goto end;
+ }
+ haveParentOption = true;
+ break;
+
+ case ALERT_TITLE:
+ break;
+
+ case ALERT_TYPE:
+ /* not sure about UTF translation here... */
+ if (Tcl_GetIndexFromObj( interp, objv[i + 1], movableTypeStrings,
+ "value", TCL_EXACT, &typeIndex ) != TCL_OK) {
+ result = TCL_ERROR;
+ goto end;
+ }
+ switch (typeIndex) {
+ case TYPE_ABORTRETRYIGNORE:
+ paramCFStringRec.defaultText = CFSTR("Abort");
+ paramCFStringRec.cancelText = CFSTR("Retry");
+ paramCFStringRec.otherText = CFSTR("Ignore");
+ break;
+ case TYPE_OK:
+ paramCFStringRec.defaultText = CFSTR("OK");
+ break;
+ case TYPE_OKCANCEL:
+ paramCFStringRec.defaultText = CFSTR("OK");
+ paramCFStringRec.cancelText = CFSTR("Cancel");
+ break;
+ case TYPE_RETRYCANCEL:
+ paramCFStringRec.defaultText = CFSTR("Retry");
+ paramCFStringRec.cancelText = CFSTR("Cancel");
+ break;
+ case TYPE_YESNO:
+ paramCFStringRec.defaultText = CFSTR("Yes");
+ paramCFStringRec.cancelText = CFSTR("No");
+ break;
+ case TYPE_YESNOCANCEL:
+ paramCFStringRec.defaultText = CFSTR("Yes");
+ paramCFStringRec.cancelText = CFSTR("No");
+ paramCFStringRec.otherText = CFSTR("Cancel");
+ break;
+ }
+ break;
+ }
+ }
+
+ if (haveDefaultOption) {
+
+ /*
+ * Any '-default' option needs to know the '-type' option, which is why
+ * we do this here.
+ */
+
+ str = Tcl_GetStringFromObj( objv[indexDefaultOption + 1], NULL );
+ if (Tcl_GetIndexFromObj( interp, objv[indexDefaultOption + 1],
+ movableButtonStrings, "value", TCL_EXACT,
+ &defaultButtonIndex ) != TCL_OK) {
+ result = TCL_ERROR;
+ goto end;
+ }
+
+ /* Need to map from "ok" etc. to 1, 2, 3, right to left. */
+
+ defaultNativeButtonIndex =
+ buttonIndexAndTypeToNativeButtonIndex[typeIndex][defaultButtonIndex];
+ if (defaultNativeButtonIndex == 0) {
+ Tcl_SetObjResult( interp,
+ Tcl_NewStringObj( "Illegel default option", -1 ));
+ result = TCL_ERROR;
+ goto end;
+ }
+ paramCFStringRec.defaultButton = defaultNativeButtonIndex;
+ }
+ SetThemeCursor( kThemeArrowCursor );
+
+ if (haveParentOption) {
+ TkWindow *winPtr;
+ MacDrawable *macWin;
+ WindowRef windowRef;
+ EventTargetRef notifyTarget;
+ EventHandlerUPP handler;
+ CallbackUserData data;
+ const EventTypeSpec kEvents[] = {
+ {kEventClassCommand, kEventProcessCommand}
+ };
+
+ winPtr = (TkWindow *) tkwin;
+
+ /*
+ * Create the underlying Mac window for this Tk window.
+ */
+
+ macWin = (MacDrawable *) winPtr->window;
+ windowRef = GetWindowFromPort( TkMacOSXGetDrawablePort((Drawable) macWin) );
+ notifyTarget = GetWindowEventTarget( windowRef );
+ osError = CreateStandardSheet( alertType, messageTextCF,
+ finemessageTextCF, &paramCFStringRec,
+ notifyTarget, &dialogRef );
+ if(osError != noErr) {
+ result = TCL_ERROR;
+ goto end;
+ }
+ data.windowRef = windowRef;
+ data.buttonIndex = 1;
+ handler = NewEventHandlerUPP( AlertHandler );
+ InstallEventHandler(notifyTarget, handler,
+ GetEventTypeCount(kEvents),
+ kEvents, &data, NULL );
+ osError = ShowSheetWindow( GetDialogWindow(dialogRef), windowRef );
+ if(osError != noErr) {
+ result = TCL_ERROR;
+ goto end;
+ }
+ osError = RunAppModalLoopForWindow( windowRef );
+
+ itemHit = data.buttonIndex;
+ DisposeEventHandlerUPP( handler );
+ } else {
+ osError = CreateStandardAlert( alertType, messageTextCF,
+ finemessageTextCF, &paramCFStringRec, &dialogRef );
+ if(osError != noErr) {
+ result = TCL_ERROR;
+ goto end;
+ }
+ osError = RunStandardAlert( dialogRef, NULL, &itemHit );
+ }
+ if(osError == noErr) {
+ int ind;
+
+ /*
+ * Map 'itemHit' (1, 2, 3) to descriptive text string.
+ */
+
+ ind = nativeButtonIndexAndTypeToButtonIndex[typeIndex][itemHit];
+ Tcl_SetObjResult( interp,
+ Tcl_NewStringObj( movableButtonStrings[ind], -1 ));
+ } else {
+ result = TCL_ERROR;
+ }
+
+ end:
+ if (finemessageTextCF != NULL) {
+ CFRelease( finemessageTextCF );
+ }
+ if (messageTextCF != NULL) {
+ CFRelease( messageTextCF );
+ }
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * AlertHandler --
+ *
+ * Carbon event handler for the Standard Sheet dialog.
+ *
+ * Results:
+ * OSStatus if event handled or not.
+ *
+ * Side effects:
+ * May set userData.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static OSStatus
+AlertHandler( EventHandlerCallRef callRef, EventRef eventRef, void *userData )
+{
+ OSStatus result = eventNotHandledErr;
+ HICommand cmd;
+ CallbackUserData *dataPtr = (CallbackUserData *) userData;
+
+ GetEventParameter( eventRef, kEventParamDirectObject, typeHICommand,
+ NULL, sizeof(cmd), NULL, &cmd );
+ switch (cmd.commandID) {
+ case kHICommandOK:
+ dataPtr->buttonIndex = 1;
+ result = noErr;
+ break;
+ case kHICommandCancel:
+ dataPtr->buttonIndex = 2;
+ result = noErr;
+ break;
+ case kHICommandOther:
+ dataPtr->buttonIndex = 3;
+ result = noErr;
+ break;
+ }
+ if (result == noErr) {
+ result = QuitAppModalLoopForWindow( dataPtr->windowRef );
+ }
+ return result;
+}