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.