ftp.nice.ch/pub/next/developer/resources/classes/PopUpMenu.0.2.s.tar.gz#/PopUpMenu/PowerMenu.m

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.