This is PowerMenu.m in view mode; [Download] [Up]
#import "PowerMenu.h" #import <appkit/Application.h> #import <dpsclient/psops.h> #import <dpsclient/wraps.h> #import <appkit/afm.h> #import <appkit/Box.h> #import <appkit/nextstd.h> #import <appkit/Text.h> #import <appkit/NXImage.h> // uncomment the below to get more debugging messages //#define DEBUGIT 1 #define BOX 1 //#define HIGHLIGHT_BY_COMPOSITE 1 #define WINDOW_MASK (NX_RMOUSEUPMASK|NX_RMOUSEDRAGGEDMASK|NX_MOUSEDRAGGEDMASK|\ NX_MOUSEENTEREDMASK|NX_MOUSEEXITEDMASK) void printrect(const char* msg, const NXRect *r) { printf("%s: %f %f %f %f\n", msg, NX_X(r), NX_Y(r), NX_WIDTH(r), NX_HEIGHT(r)); } #define PULLMENUARROWWIDTH pullmenuIconDim static id pullmenuIcon = nil; static float pullmenuIconDim = 0.0; static NXSize pullmenuIconSize; void NXDrawColoredArrow(NXRect *r, NXColor topShadowColor, NXColor bottomShadowColor, float shadowThickness, int orientation, BOOL isFlipped) { PSnewpath(); NXSetColor(topShadowColor); PSsetlinewidth(0.0); PSmoveto(0.0, 0.0); PSlineto(0.5, 0.5); PSlineto(0.0, 1.0); PSclosepath(); return; } void NXDrawColoredButton(NXRect *r, NXColor topColor, NXColor bottomColor, float thickness, BOOL isFlipped) { int i, nRectsToDraw; NXColor colors[100]; // this is bogus int sides[100]; NXRect aRect; NXRect bRect; if (thickness <= 0.0 || r == (NXRect*)0) return; nRectsToDraw = ((int)thickness * 4); for (i = 0; i < nRectsToDraw; i+=4) { sides[i] = NX_XMIN; sides[i+1] = (isFlipped)?NX_YMIN:NX_YMAX; sides[i+2] = NX_XMAX; sides[i+3] = (isFlipped)?NX_YMAX:NX_YMIN;; colors[i] = topColor; colors[i+1] = topColor; colors[i+2] = bottomColor; colors[i+3] = bottomColor; } aRect = *r; for (i = 0; i < nRectsToDraw; i++) { NXDivideRect(&aRect, &bRect, 1.0, sides[i]); NXSetColor(colors[i]); NXRectFill(&bRect); } } // our stupid little algorthm is well, stupid, I wonder what Motif does.... void NXGetColors(NXColor background, NXColor *foreground, NXColor *topShadow, NXColor *bottomShadow, NXColor *select) { float h, s, b, a; NXConvertColorToHSBA(background, &h, &s, &b, &a); if (foreground != (NXColor*)0) { float newB = b; *foreground = NXConvertHSBAToColor(h, s, newB, a); } if (topShadow != (NXColor*)0) { float newB = 1.33 * b; *topShadow = NXConvertHSBAToColor(h, s, newB, a); } if (bottomShadow != (NXColor*)0) { float newB = 1.0 / 3.0 * b; *bottomShadow = NXConvertHSBAToColor(h, s, newB, a); } if (select != (NXColor*)0) { float newB = b; *select = NXConvertHSBAToColor(h, s, newB, a); } return; } @implementation PowerMenuPrimitiveCell // the default highlight behavior is crude, use NX_HIGHLIGHT // contrary to what Motif does with it's highlighting rectangle :-) - highlight:(const NXRect *)cellFrame inView:controlView lit:(BOOL)flag { float edgeThickness = borderWidth + highlightThickness + shadowThickness; NXRect r; BOOL oldHighlightFlag = isHighlighted; [self setIsHighlighted:flag]; if (oldHighlightFlag != isHighlighted) { r = *cellFrame; NXInsetRect(&r, edgeThickness, edgeThickness); NXHighlightRect(&r); } return self; } - setEnabled:(BOOL)f { [self setSensitive:f]; return self; } - (BOOL)isEnabled { return sensitive; } - setBottomShadowColor:(NXColor)c { bottomShadowColor = c; return self; } - (NXColor)bottomShadowColor { return bottomShadowColor; } - setTopShadowColor:(NXColor)c { topShadowColor = c; return self; } - (NXColor)topShadowColor { return topShadowColor; } - setShadowThickness:(float)sst { shadowThickness = sst; return self; } - (float)shadowThickness { return shadowThickness; } - setHighlightThickness:(float)sht { highlightThickness = sht; return self; } - (float)highlightThickness { return highlightThickness; } - setBorderWidth:(float)bw { borderWidth = bw; return self; } - (float)borderWidth { return borderWidth; } - setSensitive:(BOOL)f { sensitive = f; return self; } - (BOOL)isSensitive { return sensitive; } - (BOOL)sensitive { return sensitive; } - highlight { return [self setIsHighlighted:YES]; } - unhighlight { return [self setIsHighlighted:NO]; } - setIsHighlighted:(BOOL)f { isHighlighted = f; return self; } - (BOOL)isHighlighted { return isHighlighted; } - init { [super init]; borderWidth = 1.0; isHighlighted = NO; sensitive = YES; bottomShadowColor = NX_COLORDKGRAY; highlightColor = NX_COLORWHITE; highlightThickness = 2.0; shadowThickness = 2.0; topShadowColor = NX_COLORWHITE; return self; } - drawSelf:(const NXRect*)cellFrame inView:aView { return [self drawInside:cellFrame inView:aView]; } - drawShadow:(const NXRect*)cellFrame inView:aView { float edgeThickness = borderWidth + highlightThickness + shadowThickness; NXRect r; if (shadowThickness > 0.0 && [self isSensitive]) { r = *cellFrame; edgeThickness = borderWidth + highlightThickness; NXInsetRect(&r, edgeThickness, edgeThickness); NXDrawColoredButton(cellFrame, topShadowColor, bottomShadowColor, shadowThickness, [aView isFlipped]); } return self; } - drawHighlight:(const NXRect*)cellFrame inView:aView { float edgeThickness = borderWidth + highlightThickness + shadowThickness; NXRect r; if (isHighlighted) { r = *cellFrame; edgeThickness = borderWidth; NXInsetRect(&r, edgeThickness, edgeThickness); NXSetColor(highlightColor); NXFrameRectWithWidth(&r, highlightThickness); } return self; } - drawInside:(const NXRect*)cellFrame inView:aView { [self drawShadow:cellFrame inView:aView]; [self drawHighlight:cellFrame inView:aView]; return self; } - computeColors:(NXColor)newBackgroundColor; { return self; } @end @implementation PowerMenuCell - highlight:(const NXRect *)cellFrame inView:controlView lit:(BOOL)flag { BOOL oldFlag = [self isHighlighted]; [self setIsHighlighted:flag]; if ([self isEnabled]) { [self drawInside:cellFrame inView:controlView]; } return self; } + initialize { pullmenuIcon = [NXImage findImageNamed:"NXmenuArrow"]; if (pullmenuIcon == nil) { fprintf(stderr, "Could not find pull menu icon\n"); } [pullmenuIcon getSize:&pullmenuIconSize]; //printf("menu icon size is %f %f \n", s.width, s.height); // this icon has a width and height that are different pullmenuIconDim = MAX(pullmenuIconSize.width, pullmenuIconSize.height); return [super initialize]; } - (const char*)title { return [super stringValue]; } - (BOOL)hasSubmenu { if (submenu != nil) return YES; return NO; } - (BOOL)hasIcon { return (icon != nil) ? YES : NO; } - setIcon:(const char*)iconName { return self; } - setAssumeIconDim:(float)f { pretendIconDim = f; return self; } - init { [super init]; [super initTextCell:"menu item"]; [super setAlignment:NX_LEFTALIGNED]; [super setEnabled:YES]; [super setBordered:NO]; [super setWrap:NO]; inactiveForegroundColor = NX_COLORDKGRAY; alwaysDisplayWithForeground = NO; pretendIconDim = -1.0; icon = nil; height = -1.0; shortcut = NULL; submenu = nil; alignment = NX_LEFTALIGN; marginBottom = 0.0; marginTop = 0.0; marginLeft = 0.0; marginRight = 0.0; marginHeight = 2.0; marginWidth = 2.0; return self; } - setLabel:(const char*)l { [super setStringValue:l]; [super setLabelString:l]; return self; } - titleOnly { alwaysDisplayWithForeground = YES; [self setEnabled:NO]; return self; } - blank { [self setLabel:""]; [self titleOnly]; return self; } - setSeparator:(BOOL)f { isSeparator = f; [self setEnabled:NO]; return self; } - (BOOL)isSeparator { return isSeparator; } - separator { [self setSeparator:YES]; return self; } - drawLabel:(const NXRect*)cellFrame inView:aView { NXRect r; if (![self isSeparator]) { [super drawLabel:cellFrame inView:aView]; return self; } else { // if we are a separator, draw a thing black line // that doesn't quite reach the edges of the cell r = *cellFrame; NX_WIDTH(&r) -= 4.0; NX_X(&r) += 2.0; PSgsave(); NXSetColor(NX_COLORBLACK); PSsetlinewidth(0.15); PSnewpath(); PSmoveto(NX_X(&r), NX_MIDY(&r)); PSlineto(NX_MAXX(&r), NX_MIDY(&r)); PSstroke(); PSgrestore(); } return self; } - drawInside:(const NXRect *)cellFrame inView:controlView { NXRect r; NXPoint imageOrigin; [super drawInside:cellFrame inView:controlView]; // draw the icon if there is one if ([self hasIcon]) { //imageOrigin.x = 1.0; //imageOrigin.y = 1.0; // clip to the rect for the icon. this should // control those big icons r = *cellFrame; [self getIconRect:&r]; PSgsave(); NXRectClip(&r); [icon composite:NX_SOVER toPoint:&imageOrigin]; PSgrestore(); } return self; } - drawSelf:(const NXRect*)cellFrame inView:aView { [super drawSelf:cellFrame inView:aView]; return self; } - (const char*)label { return [self title]; } - free { return [super free]; } - setBezeled:(BOOL)f { return self; } - setBordered:(BOOL)f { return self; } - calcCellSize:(NXSize*)s { NXSize s1; Font *f; const char *t = [self label]; float edgeThickness = borderWidth + highlightThickness + shadowThickness; s->width = 2.0 * (marginWidth + edgeThickness) + marginLeft + marginRight; s->height = 2.0 * (marginHeight + edgeThickness) + marginTop + marginBottom; f = [self font]; s->height += [f pointSize]; s->width += [f getWidthOf:t]; // if the icon is bigger than MAXICONDIM then // clip to MAXICONDIM if ([self hasIcon]) { [icon getSize:&s1]; s->width += 1.0; // 1 pixel to right of icon if there is one } // make space for a pullmenu arrow s->width += PULLMENUARROWWIDTH; s->width += 2.0; // left of pullmenu arrow return self; } - getPullmenuArrowRect:(NXRect*)r { [self getDrawRect:r]; NX_X(r) = NX_MAXX(r) - (PULLMENUARROWWIDTH); NX_WIDTH(r) = PULLMENUARROWWIDTH; NX_Y(r) += (NX_HEIGHT(r) - PULLMENUARROWWIDTH) / 2.0; NX_HEIGHT(r) = PULLMENUARROWWIDTH; return self; } - getIconRect:(NXRect*)r { if (![self hasIcon]) { NXSetRect(r, 0.0, 0.0, 0.0, 0.0); return self; } return self; } - getTitleRect:(NXRect*)r { [self getDrawRect:r]; NX_X(r) += marginLeft; NX_WIDTH(r) -= (marginLeft + marginRight); NX_Y(r) += marginTop; NX_HEIGHT(r) -= (marginTop + marginBottom); if (pretendIconDim > 0.0) { NX_X(r) += (pretendIconDim + 1.0); } // chop of the end for the pullmenu arrow NX_WIDTH(r) -= (PULLMENUARROWWIDTH + 2.0); return self; } - getDrawRect:(NXRect*)r { NX_WIDTH(r) -= (2.0 * marginWidth); NX_HEIGHT(r) -= (2.0 * marginHeight); NX_X(r) += marginWidth; NX_Y(r) += marginHeight; return self; } - (const char*)icon { if (submenu != nil) return "blah"; return NULL; } - submenu { return submenu; } - setSubmenu:s { submenu = s; return self; } @end @implementation PowerMenuView - defaultFont { return defaultFont; } - selectedCell { return selectedCell; } - selectCellAt:(int)row :(int)col { selectedCol = col; if (row >= 0 && row < numCells) { selectedRow = row; selectedCell = [self cellAt:row :col]; } else { selectedCell = nil; selectedRow = -1; } return self; } - dump { int i; if (cellsAreDirty) [self recache]; printf("number of cells: %d\n", numCells); for (i = 0; i < numCells; i++) { printf("Cell %2d: top: %f label: %s\n", i, topOfCells[i], [cells[i] label]); } return self; } - recache { int i; if (topOfCells == NULL) NX_ZONEMALLOC([self zone], topOfCells, float, numCells); else NX_ZONEREALLOC([self zone], topOfCells, float, numCells); for (i = 0; i < numCells; i++) topOfCells[i] = i * defaultCellHeight; cellsAreDirty = NO; return self; } - setCascadeMenuCellClass:newClass { id oldClass = cascadeMenuCellClass; cascadeMenuCellClass = newClass; return oldClass; } - setMenuCellClass:newClass { id oldClass = menuCellClass; menuCellClass = newClass; return oldClass; } - drawCells:(const NXRect*)r :(int)count { int i; NXRect cellRect; float edgeOffset = borderWidth + highlightThickness + shadowThickness; if (cellsAreDirty) [self recache]; // we should clip to the cell rectangle, but do this later [self getBounds:&bounds]; for (i = 0; i < numCells; i++) { // account for shadows NXSetRect(&cellRect, edgeOffset, edgeOffset + i * defaultCellHeight, defaultCellWidth, defaultCellHeight); //printrect("cell drawSelf: ", &cellRect); [cells[i] drawSelf:&cellRect inView:self]; } return self; } - addCascadeSelectionAt:(int)index { id newCell; int i; if (index < 0 || index > numCells) return nil; newCell = [[cascadeMenuCellClass allocFromZone:[self zone]] init]; // fill up the cell with goodies [newCell setCascadeIcon:[NXImage findImageNamed:"NXmenuArrow"]]; // allocate more space in the cache if (numCells == 0) NX_ZONEMALLOC([self zone], cells, PowerMenuCell*, 1); else NX_ZONEREALLOC([self zone], cells, PowerMenuCell*, numCells + 1); // push all cells over by 1 starting at index if (index > 0) { for (i = numCells; i >= index; i--) cells[i] = cells[i - 1]; } // add the new cell cells[index] = newCell; numCells++; cellsAreDirty = YES; return newCell; } - addSelectionAt:(int)index // should return the new cell { id newCell; int i; if (index < 0 || index > numCells) return nil; newCell = [[menuCellClass allocFromZone:[self zone]] init]; // fill up the cell with goodies // allocate more space in the cache if (numCells == 0) NX_ZONEMALLOC([self zone], cells, PowerMenuCell*, 1); else NX_ZONEREALLOC([self zone], cells, PowerMenuCell*, numCells + 1); // push all cells over by 1 starting at index if (index > 0) { for (i = numCells; i >= index; i--) cells[i] = cells[i - 1]; } // add the new cell cells[index] = newCell; numCells++; cellsAreDirty = YES; return newCell; } - makeCellAt:(int)row :(int)col { id newCell; int i; if (row < 0 || row > numCells) return nil; newCell = [[menuCellClass allocFromZone:[self zone]] init]; // fill up the cell with goodies [newCell setHighlightThickness:0.0]; // allocate more space in the cache cells = (PowerMenuCell**)realloc(cells, (numCells + 1) * sizeof(PowerMenuCell*)); // push all cells over by 1 starting at index for (i = numCells + 1; i > row; i--) cells[i] = cells[i - 1]; // add the new cell cells[row] = newCell; numCells++; cellsAreDirty = YES; return newCell; } - cellAt:(int)row :(int)col { if (row < 0 || row >= numCells) return nil; return (id)cells[row]; } - drawSelf:(const NXRect*)r :(int)c { NXSetColor(backgroundColor); NXRectFill(&r[0]); if (cellsAreDirty) [self recache]; [super drawSelf:r :c]; [self drawCells:r :c]; return self; } - initFrame:(const NXRect*)r { [super initFrame:r]; [super setFlipped:YES]; // inherited resources borderWidth = 0.0; highlightThickness = 0.0; drawButtonBorder = YES; cells = NULL; topOfCells = NULL; numCells = 0; selectedCell = nil; selectedRow = -1; selectedCol = -1; defaultTarget = nil; defaultCellHeight = 14.0 + 4.0; defaultCellWidth = 50.0; menuCellClass = [PowerMenuCell class]; cascadeMenuCellClass = [PowerMenuCascadeCell class]; defaultFont = [Font newFont:"Times-Roman" size:14.0]; backgroundColor = NX_COLORLTGRAY; foregroundColor = NX_COLORBLACK; return self; } - setMenu:m { menu = m; return self; } - menu { return menu; } // this should immediately highlight the cell ie // does not require a separate draw command - highlightCellAt:(int)r :(int)c lit:(BOOL)f { NXRect r1; id w; if (r >= 0 && r < numCells) { if ([cells[r] isEnabled] && ![cells[r] isSeparator] && [cells[r] isSensitive] && [cells[r] isHighlighted] != f) { w = [self window]; if (cellsAreDirty) [self recache]; [self getCellFrame:&r1 at:r :c]; NXSetColor(backgroundColor); [w disableDisplay]; [self lockFocus]; NXRectFill(&r1); [cells[r] highlight:&r1 inView:self lit:f]; [self unlockFocus]; [[w reenableDisplay] flushWindow]; } } return self; } - sizeToCells { return self; } - sizeToFit { float newWidth; float newHeight; int i; NXSize s; float edgeThickness = borderWidth + shadowThickness; newWidth = minimumMenuWidth; newHeight = minimumMenuHeight; if (numCells > 0) { newWidth = -1.0; newHeight = -1.0; for (i = 0; i < numCells; i++) { [cells[i] calcCellSize:&s]; defaultCellWidth = MAX(defaultCellWidth, s.width); defaultCellHeight = MAX(defaultCellHeight, s.height); } newWidth = defaultCellWidth; newHeight = numCells * defaultCellHeight; } // account for any borders newWidth += 2.0 * edgeThickness; newHeight += 2.0 * edgeThickness; // set the new size [self sizeTo:newWidth :newHeight]; return self; } - getCellFrame:(NXRect*)rect at:(int)r :(int)c { float edgeOffset = borderWidth + highlightThickness + shadowThickness; NXSetRect(rect, edgeOffset, edgeOffset + r * defaultCellHeight, defaultCellWidth, defaultCellHeight); return self; } - getRow:(int *)row andCol:(int *)col ofCell:aCell { int i; *col = 0; for (i = 0; i < numCells; i++) if ( (id)cells[i] == (id)aCell ) { *row = i; return self; } return nil; } - selectCell:c { selectedCell = c; if (selectedCell != nil) { if([self getRow:&selectedRow andCol:&selectedCol ofCell:selectedCell] == nil) return nil; return self; } return nil; } - getRow:(int *)row andCol:(int *)col forPoint:(const NXPoint *)aPoint { *row = -1; *col = -1; if (aPoint->x < NX_X(&bounds) && aPoint->x > NX_MAXX(&bounds)) return nil; if (aPoint->y < NX_Y(&bounds) && aPoint->y > NX_MAXY(&bounds)) return nil; if (cellsAreDirty) [self recache]; if (numCells > 0) { *col = 0; *row = (aPoint->y + borderWidth + highlightThickness + shadowThickness) / defaultCellHeight; return cells[*row]; } return nil; } @end @implementation PowerMenu + newPopupMenu { return [self newPopupMenuFromZone:NXDefaultMallocZone()]; } + newPopupMenuFromZone:(NXZone*)zone { id newMenu = nil; newMenu = [[PowerMenu allocFromZone:zone] init]; return newMenu; } + newSubmenuOf:theSupermenu attachToCell:(int)cellIndex { return [self newSubmenuOf:theSupermenu attachToCell:cellIndex fromZone:NXDefaultMallocZone()]; } + newSubmenuOf:theSupermenu attachToCell:(int)cellIndex fromZone:(NXZone*)zone { id newSubmenu = nil; newSubmenu = [[self allocFromZone:zone] init]; [newSubmenu setSupermenu:theSupermenu]; [theSupermenu addSubmenu:newSubmenu toSelection:cellIndex]; [newSubmenu setDefaultTarget:[theSupermenu defaultTarget]]; [newSubmenu setDefaultAction:[theSupermenu defaultAction]]; return newSubmenu; } - setSupermenu:sm { supermenu = sm; return self; } - addSubmenu:sm toSelection:(int)i { id cell = [self selectionAt:i]; if (sm == nil) return self; if (cell != nil && [cell isKindOf:[PowerMenuCascadeCell class]]) { [cell setSubmenu:sm]; [sm setSupermenu:self]; } return self; } - supermenu { return supermenu; } - (SEL)defaultAction { return defaultMsg; } - defaultTarget { return defaultTarget; } - dump { return [matrix dump]; } - init { NXRect r; defaultTarget = nil; defaultCellHeight = 14.0 + 4.0; defaultCellWidth = 50.0; attachedMenu = nil; supermenu = nil; window = [[self initContent:NULL style:NX_PLAINSTYLE backing:NX_RETAINED buttonMask:0 defer:NO] reenableDisplay]; [self setDelegate:self]; [self addToEventMask:WINDOW_MASK]; #ifdef BOX boxView = [[Box alloc] init]; [boxView setTitlePosition:NX_NOTITLE]; [boxView setBorderType:NX_LINE]; [boxView setOffsets:0.0 :0.0]; #endif view = matrix = [[PowerMenuView allocFromZone:[window zone]] initFrame:&r]; [matrix setMenu:self]; #ifdef BOX [[self setContentView:boxView] free]; [[boxView setContentView:matrix] free]; [boxView setNextResponder:self]; #else [[self setContentView:matrix] free]; #endif [matrix setNextResponder:self]; lastItem = 0; lastrow = 0; attachedMenu = nil; cellsAreDirty = YES; lastSelection = -1; return self; } - (Font*)defaultFont { return [matrix defaultFont]; } - window { return window; } - setMenuPopupPoint:(const NXPoint*)p { popupPoint = *p; return self; } - popUp:aView { NXPoint aPoint; NXEvent *nextEvent; NXRect rect; BOOL highlightInitialCell = YES; if (cellsAreDirty) { [self recache]; } [matrix sizeToFit]; [matrix sizeToCells]; #ifdef BOX [boxView sizeToFit]; #endif [[self contentView] getBounds:&rect]; [self sizeWindow:NX_WIDTH(&rect) :NX_HEIGHT(&rect)]; [self display]; // if bogus lastSelection, force it to first menu item if (lastSelection < 0 /* || lastSelection > */) lastSelection = 0; [matrix getCellFrame:&rect at:lastSelection :0]; [matrix convertRect:&rect toView:nil]; if (supermenu != nil) { // we must be a submenu, send message to supermenu to get popup location [supermenu getLocation:&aPoint forSubmenu:self]; // offset self menu to account for mouse popping up in middle of a line // for a submenu. We don't adjust x location because we want it to pop up // besides the supermenu's menu. aPoint.y -= (NX_Y(&rect) + NX_HEIGHT(&rect) / 2.0); highlightInitialCell = NO; // a submenu popup means mouse in supermenu window lastrow = -1; } else { // we are the main menu [[aView window] getMouseLocation:&aPoint]; [[aView window] convertBaseToScreen:&aPoint]; aPoint.x -= NX_WIDTH(&frame) / 2.0; aPoint.y -= (NX_Y(&rect) + NX_HEIGHT(&rect) / 2.0); highlightInitialCell = YES; } [self moveTo:aPoint.x :aPoint.y]; [self display]; [self orderFront:self]; if (highlightInitialCell) { // we should highlight a cell here [matrix lockFocus]; [matrix highlightCellAt:lastSelection :0 lit:YES]; [matrix unlockFocus]; } shouldLoop = YES; // only get events if your the top supermenu dude! if (supermenu == nil) { do { // location point is in terms of the base window that we popped up on top of nextEvent = [NXApp getNextEvent:WINDOW_MASK]; aPoint = nextEvent->location; switch (nextEvent->type) { case NX_RMOUSEUP: shouldLoop = NO; [self rightMouseUp:&aPoint ofView:aView]; [self popDown:self]; break; case NX_MOUSEDRAGGED: printf("left mouse dragged\n"); break; case NX_RMOUSEDRAGGED: [self trackMouse:&aPoint ofView:aView]; break; default: break; } } while(shouldLoop); } return self; } - addSelectionAt:(int)index { cellsAreDirty = YES; return [matrix addSelectionAt:index]; } - addCascadeSelectionAt:(int)index { cellsAreDirty = YES; return [matrix addCascadeSelectionAt:index]; } - setMenuCellClass:newClass { return [matrix setMenuCellClass:newClass]; } // this should return screen coordinates where the popup will // want to vertically center on - getLocation:(NXPoint *)theLocation forSubmenu:aSubmenu { NXRect rect; #ifdef DEBUGIT printf("finding attachedMenu location using lastrow (%d)\n", lastrow); #endif [matrix getCellFrame:&rect at:lastrow :0]; [matrix convertRect:&rect toView:nil]; theLocation->x = NX_MAXX(&frame); // + 1.0; theLocation->y = NX_Y(&frame) + NX_Y(&rect) + NX_HEIGHT(&rect)/2.0 ; return self; } - popDown:sender { // popdown attached menus if (attachedMenu != nil) [attachedMenu popDown:sender]; attachedMenu = nil; // unlight selection [matrix lockFocus]; [matrix highlightCellAt:lastrow :0 lit:NO]; [matrix unlockFocus]; // get self off the screen [self orderOut:self]; return self; } - setDefaultAction:(SEL)msg { defaultMsg = msg; return self; } - setTarget:t { return [self setDefaultTarget:t]; } - setDefaultTarget:t { defaultTarget = t; return self; } - selectionAt:(int)index { return [matrix cellAt:index :0]; } - cellAt:(int)row :(int)col { return [matrix cellAt:row :col]; } - recache { [matrix recache]; cellsAreDirty = NO; return self; } // viewPoint coordinates are what comes from the event structure directly // ie window base coordinates for the top level menu // aView should be the view where the original mouse down initiated menu action - (BOOL)trackMouse:(NXPoint*)viewPoint ofView: aView { id cell; NXRect rect; NXPoint intPoint; int row, col; BOOL rval=NO; NXPoint matrixPoint; // convert viewPoint from aView's window base coords to matrix's user coords matrixPoint = intPoint = *viewPoint; // aView's window is the only window that can convert viewPoint to screen coords [[aView window] convertBaseToScreen:&matrixPoint]; [matrix getBounds:&rect]; // convert viewPoint into the coordinates system of the matrix bounds, ugh! [self convertScreenToBase:&matrixPoint]; [matrix convertPoint:&matrixPoint fromView:nil]; if (NXMouseInRect(&matrixPoint, &rect, [matrix isFlipped])) { // then the point is in self's matrix frame, figure out which cell this maps to if([matrix getRow: &row andCol: &col forPoint: &matrixPoint] != nil) { // if lastrow==row, then just return since we are // already highlighted and we don't want to do it // twice, UNLESS this cell has a menu and it's not // popped up yet cell = [matrix cellAt:row :0]; if(lastrow==row && attachedMenu==nil && [cell hasSubmenu]) { attachedMenu = [cell submenu]; [attachedMenu popUp:aView]; } if(lastrow==row && ![cell isHighlighted]) { [matrix lockFocus]; [matrix highlightCellAt:row :0 lit:YES]; [matrix unlockFocus]; } if(lastrow==row) return YES; // don't do unneeded highlighting [matrix lockFocus]; [matrix highlightCellAt:lastrow :0 lit: NO]; [matrix highlightCellAt:row :0 lit:YES]; [matrix unlockFocus]; [matrix selectCellAt:row :0]; lastrow = row; // new selections mean that any old attached menus should be popdowned if(attachedMenu != nil) [attachedMenu popDown:self]; if([cell hasSubmenu]) { // if there is a submenu for this item, pop it up attachedMenu = [cell submenu]; [attachedMenu popUp:aView]; } else attachedMenu=nil; } rval=YES; } else { // feed the events to any the attached menus to see if the viewPoint belongs to it if(attachedMenu != nil) { if([attachedMenu trackMouse:viewPoint ofView:aView]) { // make sure we are highlighted, ie mouse came back into an attached menu if (![[self cellAt:lastrow :0] isHighlighted]) { [matrix lockFocus]; [matrix highlightCellAt:lastrow :0 lit:YES]; [matrix unlockFocus]; } rval=YES; } else { // if mouse is not in self's window and not in an attached // menu window if([[self cellAt:lastrow :0] isHighlighted]) { [matrix lockFocus]; [matrix highlightCellAt:lastrow :0 lit:NO]; [matrix unlockFocus]; } #if 0 [attachedMenu popDown:self]; attachedMenu = nil; #endif rval=NO; } } else { // if mouse is not in self's window and there is no // attached menu if([[self cellAt:lastrow :0] isHighlighted]) { [matrix lockFocus]; [matrix highlightCellAt:lastrow :0 lit:NO]; [matrix unlockFocus]; } rval=NO; } } return rval; } - (BOOL)rightMouseUp:(NXPoint*)viewPoint ofView:aView { id cell; NXRect rect; int row, col; NXPoint matrixPoint; BOOL rval=NO; row = lastSelection; // give any attached menus a change to respond if(attachedMenu != nil) { if([attachedMenu rightMouseUp:viewPoint ofView:aView]) { lastSelection = row = lastrow; //printf("saving position %d\n", lastrow); rval = YES; } } else { // see trackMouse: for why this transformation must be done matrixPoint = *viewPoint; [[aView window] convertBaseToScreen:&matrixPoint]; [matrix getBounds:&rect]; [self convertScreenToBase:&matrixPoint]; [matrix convertPoint:&matrixPoint fromView:nil]; if (NXMouseInRect(&matrixPoint, &rect, [matrix isFlipped])) { if ([matrix getRow:&row andCol:&col forPoint:&matrixPoint] != nil) { lastSelection = row; cell = [matrix cellAt:lastSelection :0]; //printf("cell selected, title (%s), row (%d), tag (%d)\n", [cell title], lastSelection, [cell tag]); if (defaultTarget != nil && [cell action] != 0) { if ([defaultTarget respondsTo:[cell action]]) [defaultTarget perform:[cell action] with:cell]; } else if (defaultTarget != nil && defaultMsg != 0) { if ([defaultTarget respondsTo:defaultMsg]) [defaultTarget perform:defaultMsg with:cell]; } } rval = YES; } } // unlight cell so it pops up again looking okay [matrix lockFocus]; [matrix highlightCellAt:row :0 lit:NO]; [matrix unlockFocus]; return rval; } @end @implementation StupidBoxView - init { return [super init]; } - initFrame:(const NXRect*)frameRect { [super initFrame:frameRect]; hOffset = vOffset = 0.0; edge = 1.0; border = NX_BUTTONBUTTON; foregroundColor = NX_COLORBLACK; backgroundColor = NX_COLORLTGRAY; return self; } - drawSelf:(const NXRect*)r :(int)count { switch (border) { case NX_BUTTONBUTTON: printf("drawing button\n"); NXDrawButton(&bounds, &bounds); break; default: break; } return self; } - setContentView:nv { NXRect r; id ov = contentView; if (contentView != nil) { [contentView removeFromSuperview]; } contentView = nv; [self addSubview:nv]; [self getBounds:&r]; [self adjustContentViewFrame:&r toView:self]; [contentView setFrame:&r]; return ov; } // to account for the bevel or edge 3D effects - adjustContentViewFrame:(NXRect*)r toView:v { if (v == self) { NX_X(r) += 1.0; NX_Y(r) += 1.0; NX_WIDTH(r) -= 3.0; NX_HEIGHT(r) -= 3.0; } else { NX_X(r) -= 2.0; NX_Y(r) -= 2.0; NX_WIDTH(r) += 4.0; NX_HEIGHT(r) += 4.0; } return self; } - contentView { return contentView; } - sizeToFit { NXRect r; if (contentView != nil) { [contentView getBounds:&r]; [self adjustContentViewFrame:&r toView:nil]; [contentView getBounds:&r]; NX_WIDTH(&r) += 4.0; NX_HEIGHT(&r) += 4.0; printf("stupid box, sizing to %f %f\n", NX_WIDTH(&r), NX_HEIGHT(&r)); [self sizeTo:NX_WIDTH(&r) :NX_HEIGHT(&r)]; } return self; } - setTitlePosition:(int)b { return self; } - setBorderType:(int)b { border = b; return self; } - setOffsets:(float)a :(float)b { hOffset = a; vOffset = b; return self; } - setEdgeThickness:(float)e { edge = e; return self; } @end @implementation PowerMenuBar - initFrame:(const NXRect*)r { [super initFrame:r]; [super setFlipped:YES]; cell = nil; return self; } - setCell:newCell { NXSize s; float newHeight, newWidth; float edgeThickness = borderWidth + highlightThickness + shadowThickness; cell = newCell; [cell calcCellSize:&s]; NX_X(&cellRect) = edgeThickness; NX_Y(&cellRect) = edgeThickness; NX_WIDTH(&cellRect) = s.width; NX_HEIGHT(&cellRect) = s.height; newHeight = s.height + edgeThickness; newWidth = NX_WIDTH(&bounds); [self sizeTo:newWidth :newHeight]; return self; } - cell { return cell; } - drawSelf:(const NXRect*)r :(int)c { [super drawSelf:r :c]; // draw title cell if (cell != nil) { [cell drawSelf:&cellRect inView:self]; } return self; } - mouseDown:(NXEvent*)e { NXPoint p; NXPoint popupPoint; NXRect cellFrame; p = e->location; [self convertPoint:&p fromView:nil]; // if the mouse down was in the title // then do popup thing if (NXMouseInRect(&p, &cellRect, [self isFlipped])) { cellFrame = cellRect; // position submenu for specialness popupPoint.x = 0.0; popupPoint.y = 0.0; //[self setMenuPopupPoint:&popupPoint]; // the highlight will pop up the submenu if there // is one [cell highlight:&cellFrame inView:self lit:YES]; } // must be done here so unhighlight the cell cellFrame = cellRect; [cell highlight:&cellFrame inView:self lit:NO]; return self; } @end @implementation PowerMenuLabelCell - setFont:newFont { font = newFont; return self; } - font { return font; } - init { [super init]; accelerator = (char*)0; label = (char*)0; // inherited resources highlightThickness = 0.0; shadowThickness = 0.0; borderWidth = 0.0; sensitive = YES; // self resources marginBottom = 0.0; marginHeight = 2.0; marginTop = 0.0; marginLeft = 0.0; marginRight = 0.0; marginWidth = 2.0; font = [Font newFont:"Times-Roman" size:14.0]; return self; } - setLabelString:(char*)newLabel { if (label != (char*)0) { free(label); label = (char*)0; } if (newLabel == (char*)0) return self; NX_ZONEMALLOC([self zone], label, char, strlen(newLabel)+1); strcpy(label, newLabel); return self; } - (const char*)label { return label; } - free { if (label != (char*)0) free(label); return [super free]; } - drawLabel:(const NXRect*)cellFrame inView:aView { char *t; NXRect r; Font *f; // draw the text if there is some, other t = [self label]; if (t != NULL) { r = *cellFrame; [self getTitleRect:&r]; NXSetColor([aView foregroundColor]); f = [self font]; [f set]; // assumes a flipped view, handle title alignment here PSmoveto(NX_X(&r), NX_Y(&r) + 3.0 / 4.0 * NX_HEIGHT(&r)); PSshow(t); } return self; } - drawShadow:(const NXRect*)cellFrame inView:aView { if ([self isHighlighted]) [super drawShadow:cellFrame inView:aView]; return self; } - drawHighlight:(const NXRect*)cellFrame inView:aView { return self; } - drawInside:(const NXRect*)cellFrame inView:aView { [super drawInside:cellFrame inView:aView]; [self drawLabel:cellFrame inView:aView]; return self; } - getDrawRect:(NXRect*)r { float edgeThickness = borderWidth + highlightThickness; NX_WIDTH(r) -= 2.0 * (edgeThickness + marginWidth); NX_HEIGHT(r) -= 2.0 * (edgeThickness + marginHeight); NX_X(r) += (edgeThickness + marginWidth); NX_Y(r) += (edgeThickness + marginHeight); return self; } - getTitleRect:(NXRect*)r { [self getDrawRect:r]; NX_X(r) += marginLeft; NX_WIDTH(r) -= (marginLeft + marginRight); NX_Y(r) += marginTop; NX_HEIGHT(r) -= (marginTop + marginBottom); return self; } @end @implementation CoreView - setSensitive:(BOOL)f { sensitive = f; return self; } - (BOOL)isSensitive { return sensitive; } - (BOOL)sensitive { return sensitive; } - initFrame:(const NXRect*)r { [super initFrame:r]; backgroundColor = NX_COLORLTGRAY; borderColor = NX_COLORBLACK; borderWidth = 1.0; return self; } - setBorderWidth:(float)f { borderWidth = f; return self; } - (float)borderWidth { return borderWidth; } - setBackgroundColor:(NXColor)fc { backgroundColor = fc; return self; } - (NXColor)backgroundColor { return backgroundColor; } @end @implementation PrimitiveView - setShadowThickness:(float)sst { shadowThickness = sst; return self; } - (float)shadowThickness { return shadowThickness; } - setHighlightThickness:(float)sht { highlightThickness = sht; return self; } - (float)highlightThickness { return highlightThickness; } - initFrame:(const NXRect*)r; { [super initFrame:r]; // super resoures borderWidth = 0.0; // self resources bottomShadowColor = NX_COLORDKGRAY; foregroundColor = NX_COLORBLACK; highlightColor = NX_COLORWHITE; highlightThickness = 2.0; shadowThickness = 2.0; topShadowColor = NX_COLORWHITE; return self; } - computeColors:(NXColor)newBackgroundColor { return self; } - drawSelf:(const NXRect*)r :(int)c { if (shadowThickness > 0.0) NXDrawColoredButton(&r[0], topShadowColor, bottomShadowColor, shadowThickness, [self isFlipped]); return self; } - setForegroundColor:(NXColor)fc { foregroundColor = fc; return self; } - (NXColor)foregroundColor { return foregroundColor; } @end @implementation PowerMenuCascadeCell - init { [super init]; // PrimitiveCell borderWidth = 0.0; sensitive = YES; highlightThickness = 0.0; shadowThickness = 2.0; // LabelCell inherited resources marginHeight = 2.0; marginLeft = 0.0; // self resources mappingDelay = 180; // in ms [self setCascadeIcon:nil]; //[self setCascadeIcon:[NXImage findImageNamed:"NXmenuArrow"]]; return self; } - setCascadeIcon:newIcon { id oldCascadeIcon = cascadeIcon; cascadeIcon = newIcon; // adjust margin for cascade icon (or lack of) if (cascadeIcon != nil) { [cascadeIcon getSize:&cascadeIconSize]; marginRight = cascadeIconSize.width + 2.0; } else { // it is nil so reset margins marginRight = 0.0; cascadeIconSize.width = cascadeIconSize.width = 0.0; } return oldCascadeIcon; } - drawCascadeIcon:(const NXRect*)cellFrame inView:aView { NXPoint p; float edgeThicknessVert = borderWidth + shadowThickness + highlightThickness + marginHeight; float edgeThicknessHoriz = borderWidth + shadowThickness + highlightThickness + marginWidth; p.x = NX_MAXX(cellFrame) - edgeThicknessHoriz - marginRight + 1.0; // center the icon in the y direction p.y = NX_MIDY(cellFrame) + cascadeIconSize.height / 2.0; [pullmenuIcon composite:NX_SOVER toPoint:&p]; return self; } - drawInside:(const NXRect*)cellFrame inView:aView { [super drawInside:cellFrame inView:aView]; if (cascadeIcon != nil) [self drawCascadeIcon:cellFrame inView:aView]; return self; } - calcCellSize:(NXSize*)s { [super calcCellSize:s]; // account for the cascadeMenuIcon s->width += (cascadeIconSize.width + 2.0); return self; } - highlight:(const NXRect *)cellFrame inView:controlView lit:(BOOL)flag { // do the normal highlight thing for a label cell [super highlight:cellFrame inView:controlView lit:flag]; #if 0 // popup a submenu if there is one and if flag == yes if ([self isHighlighted]) { if ([self hasSubmenu]) { [[self submenu] popUp:controlView]; } } else { if ([self hasSubmenu]) { [[self submenu] popDown:self]; } } #endif return self; } @end @implementation PowerMenuPushButtonCell - init { [super init]; // PrimitiveCell inherited resources borderWidth = 0.0; sensitive = YES; highlightThickness = 2.0; shadowThickness = 2.0; // LabelCell inherited resources marginHeight = 2.0; marginWidth = 2.0; // self resources _target = nil; return self; } - setTarget:t { _target = t; return self; } - target { return _target; } - setAction:(SEL)newMsg { _msg = newMsg; return self; } - (SEL)action { return _msg; } - drawInside:(const NXRect*)cellFrame inView:aView { [super drawInside:cellFrame inView:aView]; return self; } @end @implementation PowerMenuArrowCell + newUpArrow { return [self newUpArrowFromZone:NXDefaultMallocZone()]; } + newUpArrowFromZone:(NXZone*)z { id newArrow = nil; newArrow = [[self allocFromZone:z] init]; [newArrow setDirection:NX_ARROW_UP]; return self; } + newDownArrow { return [self newDownArrowFromZone:NXDefaultMallocZone()]; } + newDownArrowFromZone:(NXZone*)z { id newArrow = nil; newArrow = [[self allocFromZone:z] init]; [newArrow setDirection:NX_ARROW_DOWN]; return self; } + newLeftArrow { return [self newLeftArrowFromZone:NXDefaultMallocZone()]; } + newLeftArrowFromZone:(NXZone*)z { id newArrow = nil; newArrow = [[self allocFromZone:z] init]; [newArrow setDirection:NX_ARROW_LEFT]; return self; } + newRightArrow { return [self newRightArrowFromZone:NXDefaultMallocZone()]; } + newRightArrowFromZone:(NXZone*)z { id newArrow = nil; newArrow = [[self allocFromZone:z] init]; [newArrow setDirection:NX_ARROW_RIGHT]; return self; } - setDirection:(int)newDir { arrowDirection = newDir; return self; } - init { [super init]; // inherited resources sensitive = YES; highlightThickness = 2.0; shadowThickness = 2.0; // self resources arrowDirection = NX_ARROW_UP; return self; } - drawInside:(const NXRect*)cellFrame inView:aView { NXRect r; // this should draw the shadow, border or // highlight that has been specified [super drawInside:cellFrame inView:aView]; // now we should draw an arrow r = *cellFrame; #if 0 NXDrawColoredArrow(&r, topShadowColor, bottomShadowColor, shadowThickness, arrowDirection, [aView isFlipped]); #endif return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.