diff options
-rw-r--r-- | macosx/tkMacOSXButton.c | 121 | ||||
-rw-r--r-- | macosx/tkMacOSXMenubutton.c | 247 |
2 files changed, 238 insertions, 130 deletions
diff --git a/macosx/tkMacOSXButton.c b/macosx/tkMacOSXButton.c index 6d89667..f134e56 100644 --- a/macosx/tkMacOSXButton.c +++ b/macosx/tkMacOSXButton.c @@ -11,7 +11,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tkMacOSXButton.c,v 1.29 2007/10/15 20:52:47 hobbs Exp $ + * RCS: @(#) $Id: tkMacOSXButton.c,v 1.30 2007/11/08 14:25:59 das Exp $ */ #include "tkMacOSXPrivate.h" @@ -21,6 +21,10 @@ #define DEFAULT_USE_TK_TEXT 0 +#define CONTROL_INITIALIZED 1 +#define FIRST_DRAW 2 +#define ACTIVE 4 + /* * Default insets for controls */ @@ -88,7 +92,6 @@ typedef struct { * Forward declarations for procedures defined later in this file: */ - static OSStatus SetUserPaneDrawProc(ControlRef control, ControlUserPaneDrawProcPtr upp); static OSStatus SetUserPaneSetUpSpecialBackgroundProc(ControlRef control, @@ -118,7 +121,6 @@ Tk_ClassProcs tkpButtonProcs = { static int bCount; -int tkPictureIsOpen; /* *---------------------------------------------------------------------- @@ -153,7 +155,7 @@ TkpCreateButton( macButtonPtr->controlTitle[0] = 0; macButtonPtr->controlTitle[1] = 0; bzero(&macButtonPtr->params, sizeof(macButtonPtr->params)); - bzero(&macButtonPtr->fontStyle,sizeof(macButtonPtr->fontStyle)); + bzero(&macButtonPtr->fontStyle, sizeof(macButtonPtr->fontStyle)); return (TkButton *)macButtonPtr; } @@ -180,7 +182,7 @@ void TkpDisplayButton( ClientData clientData) /* Information about widget. */ { - MacButton *macButtonPtr = (MacButton *)clientData; + MacButton *macButtonPtr = (MacButton *) clientData; TkButton *butPtr = (TkButton *) clientData; Tk_Window tkwin = butPtr->tkwin; CGrafPtr destPort, savePort; @@ -198,7 +200,6 @@ TkpDisplayButton( if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) { return; } - pixmap = (Pixmap) Tk_WindowId(tkwin); wasUsingControl = macButtonPtr->usingControl; @@ -712,7 +713,7 @@ TkpComputeButtonGeometry( } } - if (TkMacOSXComputeDrawParams(butPtr,&drawParams)) { + if (TkMacOSXComputeDrawParams(butPtr, &drawParams)) { xInset = butPtr->indicatorSpace + DEF_INSET_LEFT + DEF_INSET_RIGHT; yInset = DEF_INSET_TOP + DEF_INSET_BOTTOM; } else { @@ -829,7 +830,10 @@ TkMacOSXInitControl( return 1; } - mbPtr->flags |= (1 + 2); + mbPtr->flags |= (CONTROL_INITIALIZED | FIRST_DRAW); + if (IsWindowActive(mbPtr->windowRef)) { + mbPtr->flags |= ACTIVE; + } return 0; } @@ -863,6 +867,7 @@ TkMacOSXDrawControl( TkButton *butPtr = (TkButton *) mbPtr; TkWindow *winPtr; Rect paneRect, cntrRect; + int active, enabled; int rebuild; winPtr = (TkWindow *) butPtr->tkwin; @@ -913,7 +918,7 @@ TkMacOSXDrawControl( mbPtr->flags = 0; } } - if (!(mbPtr->flags & 1)) { + if (!(mbPtr->flags & CONTROL_INITIALIZED)) { if (TkMacOSXInitControl(mbPtr, destPort, gc, pixmap, &paneRect, &cntrRect)) { return; @@ -971,16 +976,23 @@ TkMacOSXDrawControl( SetControlValue(mbPtr->control, 0); } - if (!Tk_MacOSXIsAppInFront() || butPtr->state == STATE_DISABLED) { - HiliteControl(mbPtr->control, kControlInactivePart); - } else { - /* - * Use NoPart for normal and to ensure correct direct transition from - * disabled to active -state. [Bug 706446] - */ - - HiliteControl(mbPtr->control, kControlNoPart); - + active = ((mbPtr->flags & ACTIVE) != 0); + if (active != IsControlActive(mbPtr->control)) { + if (active) { + ChkErr(ActivateControl, mbPtr->control); + } else { + ChkErr(DeactivateControl, mbPtr->control); + } + } + enabled = !(butPtr->state == STATE_DISABLED); + if (enabled != IsControlEnabled(mbPtr->control)) { + if (enabled) { + ChkErr(EnableControl, mbPtr->control); + } else { + ChkErr(DisableControl, mbPtr->control); + } + } + if (active && enabled) { if (butPtr->state == STATE_ACTIVE) { if (mbPtr->params.isBevel) { HiliteControl(mbPtr->control, kControlButtonPart); @@ -997,6 +1009,8 @@ TkMacOSXDrawControl( break; } } + } else { + HiliteControl(mbPtr->control, kControlNoPart); } } UpdateControlColors(mbPtr); @@ -1013,17 +1027,20 @@ TkMacOSXDrawControl( kControlPushButtonDefaultTag, sizeof(isDefault), &isDefault); } - if (mbPtr->flags & 2) { + if (mbPtr->flags & FIRST_DRAW) { ShowControl(mbPtr->userPane); ShowControl(mbPtr->control); - mbPtr->flags ^= 2; + mbPtr->flags ^= FIRST_DRAW; } else { SetControlVisibility(mbPtr->control, true, true); Draw1Control(mbPtr->userPane); } if (mbPtr->params.isBevel) { - KillPicture(mbPtr->bevelButtonContent.u.picture); + if (mbPtr->bevelButtonContent.contentType == + kControlContentPictHandle) { + KillPicture(mbPtr->bevelButtonContent.u.picture); + } } } @@ -1073,28 +1090,30 @@ SetupBevelButton( height = butPtr->height; } - portChanged = QDSwapPort(destPort, &savePort); - mbPtr->picParams.version = -2; - mbPtr->picParams.hRes = 0x00480000; - mbPtr->picParams.vRes = 0x00480000; - mbPtr->picParams.srcRect.top = 0; - mbPtr->picParams.srcRect.left = 0; - mbPtr->picParams.srcRect.right = width; - mbPtr->picParams.srcRect.bottom = height; - mbPtr->picParams.reserved1 = 0; - mbPtr->picParams.reserved2 = 0; - mbPtr->bevelButtonContent.contentType = kControlContentPictHandle; - mbPtr->bevelButtonContent.u.picture = OpenCPicture(&mbPtr->picParams); - if (!mbPtr->bevelButtonContent.u.picture) { - TkMacOSXDbgMsg("OpenCPicture failed"); - } - tkPictureIsOpen = 1; + { + portChanged = QDSwapPort(destPort, &savePort); + mbPtr->picParams.version = -2; + mbPtr->picParams.hRes = 0x00480000; + mbPtr->picParams.vRes = 0x00480000; + mbPtr->picParams.srcRect.top = 0; + mbPtr->picParams.srcRect.left = 0; + mbPtr->picParams.srcRect.bottom = height; + mbPtr->picParams.srcRect.right = width; + mbPtr->picParams.reserved1 = 0; + mbPtr->picParams.reserved2 = 0; + mbPtr->bevelButtonContent.contentType = kControlContentPictHandle; + mbPtr->bevelButtonContent.u.picture = OpenCPicture(&mbPtr->picParams); + if (!mbPtr->bevelButtonContent.u.picture) { + TkMacOSXDbgMsg("OpenCPicture failed"); + } + tkPictureIsOpen = 1; - /* - * TO DO - There is one case where XCopyPlane calls CopyDeepMask, - * which does not get recorded in the picture. So the bitmap code - * will fail in that case. - */ + /* + * TO DO - There is one case where XCopyPlane calls CopyDeepMask, + * which does not get recorded in the picture. So the bitmap code + * will fail in that case. + */ + } if (butPtr->selectImage != NULL && (butPtr->flags & SELECTED)) { Tk_RedrawImage(butPtr->selectImage, 0, 0, width, height, pixmap, 0, 0); @@ -1109,10 +1128,12 @@ SetupBevelButton( height, 0, 0, 1); } - ClosePicture(); - tkPictureIsOpen = 0; - if (portChanged) { - QDSwapPort(savePort, NULL); + { + ClosePicture(); + tkPictureIsOpen = 0; + if (portChanged) { + QDSwapPort(savePort, NULL); + } } ChkErr(SetControlData, controlHandle, kControlButtonPart, kControlBevelButtonContentTag, @@ -1360,12 +1381,18 @@ ButtonEventProc( XEvent *eventPtr) /* Information about event. */ { TkButton *buttonPtr = (TkButton *) clientData; + MacButton *mbPtr = (MacButton *) clientData; if (eventPtr->type == ActivateNotify || eventPtr->type == DeactivateNotify) { if ((buttonPtr->tkwin == NULL) || (!Tk_IsMapped(buttonPtr->tkwin))) { return; } + if (eventPtr->type == ActivateNotify) { + mbPtr->flags |= ACTIVE; + } else { + mbPtr->flags &= ~ACTIVE; + } if ((buttonPtr->flags & REDRAW_PENDING) == 0) { Tcl_DoWhenIdle(TkpDisplayButton, (ClientData) buttonPtr); buttonPtr->flags |= REDRAW_PENDING; diff --git a/macosx/tkMacOSXMenubutton.c b/macosx/tkMacOSXMenubutton.c index fb18b2e..d728fe5 100644 --- a/macosx/tkMacOSXMenubutton.c +++ b/macosx/tkMacOSXMenubutton.c @@ -11,7 +11,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tkMacOSXMenubutton.c,v 1.16 2007/10/15 20:52:47 hobbs Exp $ + * RCS: @(#) $Id: tkMacOSXMenubutton.c,v 1.17 2007/11/08 14:25:59 das Exp $ */ #include "tkMacOSXPrivate.h" @@ -27,6 +27,9 @@ #define TK_POPUP_OFFSET 32 /* size of popup marker */ +#define FIRST_DRAW 2 +#define ACTIVE 4 + MODULE_SCOPE int TkMacOSXGetNewMenuID(Tcl_Interp *interp, TkMenu *menuInstPtr, int cascade, short *menuIDPtr); MODULE_SCOPE void TkMacOSXFreeMenuID(short menuID); @@ -76,6 +79,7 @@ static void UserPaneBackgroundProc(ControlHandle, ControlBackgroundPtr info); static int MenuButtonInitControl (MacMenuButton *mbPtr, Rect *paneRect, Rect *cntrRect ); +static void MenuButtonEventProc(ClientData clientData, XEvent *eventPtr); static int UpdateControlColors(MacMenuButton *mbPtr); static void ComputeMenuButtonControlParams(TkMenuButton *mbPtr, MenuButtonControlParams * paramsPtr); @@ -116,22 +120,17 @@ TkpCreateMenuButton( Tk_Window tkwin) { MacMenuButton *mbPtr = (MacMenuButton *) ckalloc(sizeof(MacMenuButton)); - mbPtr->userPaneBackground = PIXEL_MAGIC << 24; + + Tk_CreateEventHandler(tkwin, ActivateMask, + MenuButtonEventProc, (ClientData) mbPtr); mbPtr->flags = 0; + mbPtr->userPaneBackground = PIXEL_MAGIC << 24; mbPtr->userPane = NULL; mbPtr->control = NULL; - mbPtr->picParams.version = -2; - mbPtr->picParams.hRes = 0x00480000; - mbPtr->picParams.vRes = 0x00480000; - mbPtr->picParams.srcRect.top = 0; - mbPtr->picParams.srcRect.left = 0; - mbPtr->picParams.reserved1 = 0; - mbPtr->picParams.reserved2 = 0; - mbPtr->bevelButtonContent.contentType = kControlContentPictHandle; mbPtr->menuRef = NULL; - bzero(&mbPtr->params, sizeof(mbPtr->params)); - bzero(&mbPtr->titleParams,sizeof(mbPtr->titleParams)); + bzero(&mbPtr->titleParams, sizeof(mbPtr->titleParams)); + return (TkMenuButton *) mbPtr; } @@ -162,26 +161,25 @@ TkpDisplayMenuButton( Pixmap pixmap; MacMenuButton *mbPtr = (MacMenuButton *) butPtr; CGrafPtr destPort, savePort; - Boolean portChanged; + Boolean portChanged = false; int hasImageOrBitmap = 0, width, height; OSStatus err; ControlButtonGraphicAlignment theAlignment; Rect paneRect, cntrRect; + int active, enabled; butPtr->flags &= ~REDRAW_PENDING; if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) { return; } pixmap = (Pixmap) Tk_WindowId(tkwin); - destPort = TkMacOSXGetDrawablePort(Tk_WindowId(tkwin)); - portChanged = QDSwapPort(destPort, &savePort); TkMacOSXSetUpClippingRgn(Tk_WindowId(tkwin)); winPtr = (TkWindow *)butPtr->tkwin; paneRect.left = winPtr->privatePtr->xOff; paneRect.top = winPtr->privatePtr->yOff; - paneRect.right = paneRect.left+Tk_Width(butPtr->tkwin)-1; - paneRect.bottom = paneRect.top+Tk_Height(butPtr->tkwin)-1; + paneRect.right = paneRect.left+Tk_Width(butPtr->tkwin); + paneRect.bottom = paneRect.top+Tk_Height(butPtr->tkwin); cntrRect = paneRect; @@ -212,21 +210,29 @@ TkpDisplayMenuButton( return; } } - SetControlBounds(mbPtr->userPane,&paneRect); - SetControlBounds(mbPtr->control,&cntrRect); + SetControlBounds(mbPtr->userPane, &paneRect); + SetControlBounds(mbPtr->control, &cntrRect); + + if (butPtr->image != None) { + Tk_SizeOfImage(butPtr->image, &width, &height); + hasImageOrBitmap = 1; + } else if (butPtr->bitmap != None) { + Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height); + hasImageOrBitmap = 1; + } /* * We need to cache the title and its style */ - if (!(mbPtr->flags & 2)) { + if (!(mbPtr->flags & FIRST_DRAW)) { ControlTitleParams titleParams; int titleChanged; int styleChanged; - ComputeControlTitleParams(butPtr,&titleParams); - CompareControlTitleParams(&titleParams,&mbPtr->titleParams, - &titleChanged,&styleChanged); + ComputeControlTitleParams(butPtr, &titleParams); + CompareControlTitleParams(&titleParams, &mbPtr->titleParams, + &titleChanged, &styleChanged); if (titleChanged) { CFStringRef cf = CFStringCreateWithCString(NULL, (char*) titleParams.title, kCFStringEncodingUTF8); @@ -253,43 +259,54 @@ TkpDisplayMenuButton( sizeof(titleParams.style)); } } - if (butPtr->image != None) { - Tk_SizeOfImage(butPtr->image, &width, &height); - hasImageOrBitmap = 1; - } else if (butPtr->bitmap != None) { - Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height); - hasImageOrBitmap = 1; - } if (hasImageOrBitmap) { - mbPtr->picParams.srcRect.right = width; - mbPtr->picParams.srcRect.bottom = height; - - /* - * Set the flag to circumvent clipping and bounds problems with OS - * 10.0.4 - */ - - tkPictureIsOpen = 1; - mbPtr->bevelButtonContent.u.picture = OpenCPicture(&mbPtr->picParams); - if (!mbPtr->bevelButtonContent.u.picture) { - TkMacOSXDbgMsg("OpenCPicture failed"); - } - - /* - * TO DO - There is one case where XCopyPlane calls CopyDeepMask, - * which does not get recorded in the picture. So the bitmap code - * will fail in that case. - */ + { + destPort = TkMacOSXGetDrawablePort(Tk_WindowId(tkwin)); + portChanged = QDSwapPort(destPort, &savePort); + mbPtr->picParams.version = -2; + mbPtr->picParams.hRes = 0x00480000; + mbPtr->picParams.vRes = 0x00480000; + mbPtr->picParams.srcRect.top = 0; + mbPtr->picParams.srcRect.left = 0; + mbPtr->picParams.srcRect.bottom = height; + mbPtr->picParams.srcRect.right = width; + mbPtr->picParams.reserved1 = 0; + mbPtr->picParams.reserved2 = 0; + mbPtr->bevelButtonContent.contentType = kControlContentPictHandle; + mbPtr->bevelButtonContent.u.picture = OpenCPicture(&mbPtr->picParams); + if (!mbPtr->bevelButtonContent.u.picture) { + TkMacOSXDbgMsg("OpenCPicture failed"); + } + tkPictureIsOpen = 1; + /* + * TO DO - There is one case where XCopyPlane calls CopyDeepMask, + * which does not get recorded in the picture. So the bitmap code + * will fail in that case. + */ + } if (butPtr->image != NULL) { Tk_RedrawImage(butPtr->image, 0, 0, width, height, pixmap, 0, 0); } else { - XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, NULL, 0, 0, + GC gc; + + if (butPtr->state == STATE_DISABLED) { + gc = butPtr->disabledGC; + } else if (butPtr->state == STATE_ACTIVE) { + gc = butPtr->activeTextGC; + } else { + gc = butPtr->normalTextGC; + } + XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc, 0, 0, width, height, 0, 0, 1); } - ClosePicture(); - - tkPictureIsOpen = 0; + { + ClosePicture(); + tkPictureIsOpen = 0; + if (portChanged) { + QDSwapPort(savePort, NULL); + } + } ChkErr(SetControlData, mbPtr->control, kControlButtonPart, kControlBevelButtonContentTag, sizeof(ControlButtonContentInfo), @@ -328,25 +345,47 @@ TkpDisplayMenuButton( kControlBevelButtonGraphicAlignTag, sizeof(ControlButtonGraphicAlignment), (char *) &theAlignment); } - if (butPtr->flags & GOT_FOCUS) { - HiliteControl(mbPtr->control,kControlButtonPart); - } else { - HiliteControl(mbPtr->control,kControlNoPart); + active = ((mbPtr->flags & ACTIVE) != 0); + if (active != IsControlActive(mbPtr->control)) { + if (active) { + ChkErr(ActivateControl, mbPtr->control); + } else { + ChkErr(DeactivateControl, mbPtr->control); + } + } + enabled = !(butPtr->state == STATE_DISABLED); + if (enabled != IsControlEnabled(mbPtr->control)) { + if (enabled) { + ChkErr(EnableControl, mbPtr->control); + } else { + ChkErr(DisableControl, mbPtr->control); + } + } + if (active && enabled) { + if (butPtr->state == STATE_ACTIVE) { + if (hasImageOrBitmap) { + HiliteControl(mbPtr->control, kControlButtonPart); + } else { + HiliteControl(mbPtr->control, kControlLabelPart); + } + } else { + HiliteControl(mbPtr->control, kControlNoPart); + } } UpdateControlColors(mbPtr); - if (mbPtr->flags&2) { + if (mbPtr->flags & FIRST_DRAW) { ShowControl(mbPtr->control); ShowControl(mbPtr->userPane); - mbPtr->flags ^= 2; + mbPtr->flags ^= FIRST_DRAW; } else { - Draw1Control(mbPtr->userPane); SetControlVisibility(mbPtr->control, true, true); + Draw1Control(mbPtr->userPane); } if (hasImageOrBitmap) { - KillPicture(mbPtr->bevelButtonContent.u.picture); - } - if (portChanged) { - QDSwapPort(savePort, NULL); + if (mbPtr->bevelButtonContent.contentType == + kControlContentPictHandle) { + KillPicture(mbPtr->bevelButtonContent.u.picture); + } } } @@ -413,21 +452,9 @@ TkpComputeMenuButtonGeometry(mbPtr) mbPtr->inset = mbPtr->highlightWidth + mbPtr->borderWidth; if (mbPtr->image != None) { Tk_SizeOfImage(mbPtr->image, &width, &height); - if (mbPtr->width > 0) { - width = mbPtr->width; - } - if (mbPtr->height > 0) { - height = mbPtr->height; - } hasImageOrBitmap = 1; } else if (mbPtr->bitmap != None) { Tk_SizeOfBitmap(mbPtr->display, mbPtr->bitmap, &width, &height); - if (mbPtr->width > 0) { - width = mbPtr->width; - } - if (mbPtr->height > 0) { - height = mbPtr->height; - } hasImageOrBitmap = 1; } else { hasImageOrBitmap = 0; @@ -449,7 +476,19 @@ TkpComputeMenuButtonGeometry(mbPtr) width += 2*mbPtr->padX; height += 2*mbPtr->padY; } - + if (hasImageOrBitmap) { + if (mbPtr->width > 0) { + width = mbPtr->width; + } + if (mbPtr->height > 0) { + height = mbPtr->height; + } + mbPtr->inset = mbPtr->highlightWidth + 2; + width += (2 * mbPtr->borderWidth + 4); + height += (2 * mbPtr->borderWidth + 4); + } else { + width += TK_POPUP_OFFSET; + } if (mbPtr->indicatorOn) { mm = WidthMMOfScreen(Tk_Screen(mbPtr->tkwin)); pixels = WidthOfScreen(Tk_Screen(mbPtr->tkwin)); @@ -460,9 +499,6 @@ TkpComputeMenuButtonGeometry(mbPtr) mbPtr->indicatorHeight = 0; mbPtr->indicatorWidth = 0; } - if (!hasImageOrBitmap) { - width += TK_POPUP_OFFSET; - } Tk_GeometryRequest(mbPtr->tkwin, (int) (width + 2*mbPtr->inset), (int) (height + 2*mbPtr->inset)); @@ -640,7 +676,7 @@ MenuButtonInitControl( * Do this only if we are using bevel buttons. */ - ComputeControlTitleParams(butPtr,&mbPtr->titleParams); + ComputeControlTitleParams(butPtr, &mbPtr->titleParams); mbPtr->control = NewControl(mbPtr->windowRef, cntrRect, "\p" /* mbPtr->titleParams.title */, initiallyVisible, mbPtr->params.initialValue, @@ -706,7 +742,10 @@ MenuButtonInitControl( SetControlMaximum(mbPtr->control, 1); SetControlValue(mbPtr->control, 1); } - mbPtr->flags |= 2; + mbPtr->flags |= FIRST_DRAW; + if (IsWindowActive(mbPtr->windowRef)) { + mbPtr->flags |= ACTIVE; + } return 0; } @@ -863,7 +902,7 @@ UpdateControlColors( MacMenuButton *mbPtr) { XColor *xcolor; - TkMenuButton * butPtr = ( TkMenuButton * )mbPtr; + TkMenuButton * butPtr = (TkMenuButton *) mbPtr; /* * Under Appearance we cannot change the background of the @@ -881,3 +920,45 @@ UpdateControlColors( return false; } + +/* + *-------------------------------------------------------------- + * + * MenuButtonEventProc -- + * + * This procedure is invoked by the Tk dispatcher for various + * events on buttons. + * + * Results: + * None. + * + * Side effects: + * When it gets exposed, it is redisplayed. + * + *-------------------------------------------------------------- + */ + +static void +MenuButtonEventProc( + ClientData clientData, /* Information about window. */ + XEvent *eventPtr) /* Information about event. */ +{ + TkMenuButton *buttonPtr = (TkMenuButton *) clientData; + MacMenuButton *mbPtr = (MacMenuButton *) clientData; + + if (eventPtr->type == ActivateNotify + || eventPtr->type == DeactivateNotify) { + if ((buttonPtr->tkwin == NULL) || (!Tk_IsMapped(buttonPtr->tkwin))) { + return; + } + if (eventPtr->type == ActivateNotify) { + mbPtr->flags |= ACTIVE; + } else { + mbPtr->flags &= ~ACTIVE; + } + if ((buttonPtr->flags & REDRAW_PENDING) == 0) { + Tcl_DoWhenIdle(TkpDisplayMenuButton, (ClientData) buttonPtr); + buttonPtr->flags |= REDRAW_PENDING; + } + } +} |