home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1995 August / NEBULA.mdf / SourceCode / BrainRulers / Source / Ruler.m < prev    next >
Encoding:
Text File  |  1994-06-07  |  12.2 KB  |  469 lines

  1. // Ruler.m
  2. // A general ruler class designed to be used with the RuledScrollView class 
  3.  
  4. #import <dpsclient/psops.h>
  5. #import <libc.h>                // for atoi and strrchr
  6. #import <math.h>
  7. #import <appkit/Font.h>
  8. #import <appkit/ScrollView.h>
  9. #import <appkit/Application.h>    // for NXApp method
  10.  
  11. #import "RuledScrollView.h"        // for retrieving rulers
  12.  
  13. #import "Ruler.h"
  14. #define DEFAULTSCALESPACING (72*0.39)
  15.  
  16. @implementation Ruler
  17.  
  18.  
  19. /* instance methods */
  20. /* Initializing and freeing an instance */
  21. - initFrame:(NXRect *)frameRect
  22. {
  23.     [super initFrame:frameRect];
  24.     
  25.     scaleSpacing = DEFAULTSCALESPACING;
  26.     orientation = HORIZONTAL;
  27.     inverted = NO;
  28.     mirrored = NO;
  29.     scale = 1.0;
  30.     scaleOffset = 0;
  31.     border = 0.0;
  32.     scaleSpacing = DEFAULTSCALESPACING;
  33.     alignment = NX_CENTERED;
  34.     leftOrBottomMargin = 0;
  35.     rightOrTopMargin = 0;
  36.     backgroundColor = NXConvertRGBToColor(1.0,1.0,1.0);
  37.     backgroundGray = NX_LTGRAY;
  38.     draggableRuler = NO;
  39.     units = malloc(2*sizeof(char));
  40.     strcpy(units,"\0");
  41.     
  42.     font = [Font newFont:"Helvetica" size:12 style:0 matrix:NX_IDENTITYMATRIX];
  43.     NXTextFontInfo(font, &ascender, &descender, &lineHeight);
  44.  
  45.     return self;
  46. }
  47.  
  48. - free
  49. {
  50.     if (units) free(units);
  51.     return [super free];
  52. }
  53.  
  54. /* Drawing the view */
  55. - drawSelf:(NXRect *)rects :(int)rectCount
  56. {
  57.     char buf[25];
  58.     float r,g,b;
  59.     int    j,start,end;
  60.     float    x0,y0,x1,y1,measure, halfWidth;
  61.     float    vtextOffset,htextOffset;
  62.  
  63.     x0 = rects[0].origin.x;
  64.     y0 = rects[0].origin.y;
  65.     x1 = x0 + rects[0].size.width;
  66.     y1 = y0 + rects[0].size.height;
  67.  
  68.     NXConvertColorToRGB(backgroundColor, &r, &g, &b);
  69.     if ((r!=1.0) || (g!=1.0) || (b!=1.0)) {
  70.         PSsetrgbcolor(r,g,b);
  71.         }
  72.     else
  73.         PSsetgray(backgroundGray);
  74.     NXRectFill(&(rects[0]));
  75.  
  76.     [font set];
  77.   // draw a line along the edge of the ruler next to the 'rulered view'
  78.     PSsetgray(NX_BLACK);
  79.     PSsetlinewidth(0.0);
  80.     if (orientation == HORIZONTAL) {
  81.         if (inverted) {
  82.             PSmoveto(x0,1);
  83.             PSlineto(x1,1);
  84.             }
  85.         else {
  86.             PSmoveto(x0,bounds.size.height);
  87.             PSlineto(x1,bounds.size.height);
  88.             }
  89.         }
  90.     else {
  91.         if (inverted) {
  92.             PSmoveto(0,y0);
  93.             PSlineto(0,y1);
  94.             }
  95.         else {
  96.             PSmoveto(bounds.size.width-1,y0);
  97.             PSlineto(bounds.size.width-1,y1);
  98.             }
  99.         }
  100.     PSstroke();
  101.  
  102.   // draw measures
  103.  
  104.     if (orientation == HORIZONTAL) {
  105.         start = (int)floor((double)(x0/scaleSpacing))-2;
  106.         end = (int)ceil((double)(x1/scaleSpacing))+1;
  107.         }
  108.     else {
  109.         start = (int)floor((double)(y0/scaleSpacing))-2;
  110.         end = (int)ceil((double)(y1/scaleSpacing))+1;
  111.         }
  112.     for(j=start; j<=end; j++) {
  113.         measure = j*scale+scaleOffset;
  114.  
  115.         sprintf(buf,"%.4g",measure);
  116.         strcatn(buf,units,23-strlen(buf));
  117.         halfWidth = [font getWidthOf:buf]/2;
  118.         switch (alignment) {
  119.             case NX_LEFTALIGNED: 
  120.                 htextOffset = 2;
  121.                 break;
  122.             case NX_RIGHTALIGNED: 
  123.                 if (orientation == HORIZONTAL) 
  124.                     htextOffset = -2-(halfWidth*2);
  125.                 else 
  126.                     htextOffset =  bounds.size.width-2-(halfWidth*2);
  127.                 break;
  128.             default:    // default is centered
  129.                 if (orientation == HORIZONTAL) 
  130.                     htextOffset = -halfWidth;
  131.                 else 
  132.                     htextOffset =  bounds.size.width/2-halfWidth;
  133.             }
  134.         if (orientation == HORIZONTAL) {
  135.             if (inverted)
  136.                 vtextOffset = TEXTBASELINE;
  137.             else
  138.                 vtextOffset = bounds.size.height - lineHeight;
  139.             if (vtextOffset < TEXTBASELINE) 
  140.                 vtextOffset = TEXTBASELINE;
  141.             PSmoveto(j*scaleSpacing+leftOrBottomMargin+htextOffset+border, vtextOffset);
  142.             }
  143.         else
  144.             PSmoveto(htextOffset, j*scaleSpacing+leftOrBottomMargin-(lineHeight/2)+border);
  145.         if (j>=0)
  146.             PSshow(buf);
  147.     }    
  148.  
  149.     return self;
  150. }
  151.  
  152. /* Event handling */
  153. - (BOOL) acceptsFirstMouse 
  154. {
  155.     return YES;
  156. }
  157.  
  158. - mouseDown:(NXEvent *)theEvent
  159. {
  160.     id        rulerWindow;
  161.     int        oldMask, looping = YES;
  162.     NXRect    windowRect,tempRect,visibleRect;
  163.     NXPoint    offset, mouseLocation;
  164.     BOOL    shiftHeld = NO;
  165.     
  166.     if (!draggableRuler)
  167.         return self;
  168.     if (theEvent->flags & NX_SHIFTMASK) shiftHeld = YES;
  169.         
  170.   //    if(theEvent->data.mouse.click == 2)        
  171.     /* action for a double-click event */
  172.     
  173.     /* convert the visible rect to screen-based coordinates */
  174.     [self getVisibleRect:(NXRect *)&windowRect];
  175.     [self convertRect:&windowRect toView:nil];
  176.     [window convertBaseToScreen:&windowRect.origin];
  177.  
  178.   /* adjust for width of outline */
  179.     windowRect.size.width += 2;
  180.     windowRect.size.height += 2;
  181.     windowRect.origin.x -= 1;
  182.     windowRect.origin.y -= 1;
  183.     tempRect.size = windowRect.size;
  184.     tempRect.origin = bounds.origin;
  185.     
  186.     rulerWindow = [[Window alloc] initContent:(const NXRect *)&windowRect
  187.         style:(int)NX_PLAINSTYLE    // no title bar and frame
  188.         backing:(int)NX_BUFFERED
  189.         buttonMask:(int)0    // no controls in title bar and frame 
  190.         defer:(BOOL)NO];
  191.   
  192.   /* so we can draw the currently visible region */
  193.     [self getVisibleRect:&visibleRect];
  194.  
  195.   /* draw the ruler into the newly created window's content view */ 
  196.     [[rulerWindow contentView] lockFocus];
  197.         // account for frame and translate to currently visible region
  198.         PStranslate(1-visibleRect.origin.x,1-visibleRect.origin.y);
  199.         [self drawSelf:&bounds:0];
  200.         PStranslate(-1+visibleRect.origin.x,-1+visibleRect.origin.y);
  201.         NXFrameRect((const NXRect *)&tempRect);
  202.     [[rulerWindow contentView] unlockFocus];
  203.  
  204.     [rulerWindow orderFront:NULL];
  205.     
  206.     /* compute the offset from the image's origin to the mouse location */
  207.     offset.x = theEvent->location.x - NX_X(&windowRect)+2;
  208.     offset.y = theEvent->location.y - NX_Y(&windowRect)-2;
  209.     
  210.     /* convert the mousedown location to screen coordinates */
  211.     mouseLocation = theEvent->location;
  212.     [window convertBaseToScreen:&mouseLocation];
  213.     
  214.     /* go into the dragging loop */
  215.     oldMask = [window addToEventMask:NX_MOUSEDRAGGEDMASK];
  216.     while(looping) {
  217.         theEvent = [NXApp getNextEvent:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
  218.         if(theEvent->type == NX_MOUSEDRAGGED){
  219.             if (shiftHeld == YES) {     // constrain drag
  220.                 [rulerWindow getFrame:&tempRect];
  221.                 if (orientation == HORIZONTAL)
  222.                     [rulerWindow moveTo:tempRect.origin.x 
  223.                         :theEvent->location.y-offset.y];
  224.                 else
  225.                     [rulerWindow moveTo:theEvent->location.x-offset.x 
  226.                         :tempRect.origin.y];
  227.                 }
  228.             else
  229.                 [rulerWindow moveTo:theEvent->location.x-offset.x 
  230.                     :theEvent->location.y-offset.y];
  231.             }
  232.         else {     /* NX_MOUSEUP */
  233.             [rulerWindow free];
  234.             looping = NO;
  235.             }
  236.         }
  237.  
  238.     return self;
  239. }
  240.  
  241. /* Sizing the rulers */
  242. - setSize
  243.   // Sets the size of the view.  Invoked by the setSize method of
  244.   // RuledScrollView, which must be invoked whenever the size of
  245.   // the mainView is changed.  
  246.   // In addition to sizing the ruler, this method also sets the instance
  247.   // variables leftOrBottomMargin and rightOrTopMargin, which determine 
  248.   // the location of the origin of the ruler.  If the ruler extends over 
  249.   // the end of an adjacent ruler, the corresponding margin is set to 
  250.   // the width or height of that ruler.  (In order to extend over the 
  251.   // end of another ruler, the ruler must be a 'primary' ruler and 
  252.   // there must be no corresponding stubView.)  The intention of the 
  253.   // margin variables is to locate the origin of the ruler at the
  254.   // edge of the view that it 'rules', automatically accounting  
  255.   // for whatever stub views exist and which rulers are primary.
  256.   //
  257.   // For example, sizing a Right Ruler is done as follows:
  258.   // 1) if Vertical Rulers are primary, and there is a bottom ruler
  259.   //    and there isn't a bottom right stubView, set
  260.   //    leftOrBottomMargin to the height of the bottom ruler
  261.   // 2) if Vertical Rulers are primary, and there is a top ruler
  262.   //    and there isn't a top right stubView, set
  263.   //    rightOrTopMargin to the height of the top ruler
  264.   // 4) the height is set to the height of the main View +
  265.   //    leftOrBottomMargin + rightOrTopMargin.  The width is 
  266.   //    unchanged.
  267.    
  268. {
  269.     NXRect theRect, mainViewFrame, rulerFrame;
  270.     id leftOrBottomRuler, rightOrTopRuler, ruledScrollView, stub;
  271.  
  272.   /* Ruler's super is a clipview; it's super is RuledScrollView,
  273.    * it's docView is the main View  */
  274.     ruledScrollView = [superview superview];
  275.     if (![ruledScrollView isKindOf:[RuledScrollView class]])
  276.       // this ruler is not currently in a RuledScrollView
  277.       // (probably because ruler is hidden)
  278.         return self;
  279.     [[ruledScrollView docView] getFrame:&mainViewFrame];
  280.     [self getFrame:(NXRect *)&theRect];
  281.     leftOrBottomMargin = 0.0;
  282.     rightOrTopMargin = 0.0;
  283.     if (orientation == HORIZONTAL) {
  284.         leftOrBottomRuler = [ruledScrollView leftRuler];
  285.         if ([ruledScrollView bottomRuler] == self)
  286.             stub = [ruledScrollView bottomLeftStub];
  287.         else
  288.             stub = [ruledScrollView topLeftStub];
  289.         if (leftOrBottomRuler && (!stub) && ([ruledScrollView primaryRulers] == TOPBOTTOM)) {
  290.             [leftOrBottomRuler getFrame:&rulerFrame];
  291.             leftOrBottomMargin = rulerFrame.size.width;
  292.             }
  293.             
  294.         rightOrTopRuler = [ruledScrollView rightRuler];
  295.         if ([ruledScrollView bottomRuler] == self)
  296.             stub = [ruledScrollView bottomRightStub]; 
  297.         else
  298.             stub = [ruledScrollView topRightStub]; 
  299.         if (rightOrTopRuler && (!stub) && ([ruledScrollView primaryRulers] == TOPBOTTOM)) {
  300.             [rightOrTopRuler getFrame:&rulerFrame];
  301.             rightOrTopMargin = rulerFrame.size.width;
  302.             }
  303.         theRect.size.width = mainViewFrame.size.width 
  304.             + leftOrBottomMargin + rightOrTopMargin;
  305.         }
  306.     else {
  307.         leftOrBottomRuler = [ruledScrollView bottomRuler];
  308.         if ([ruledScrollView leftRuler] == self)
  309.             stub = [ruledScrollView bottomLeftStub];
  310.         else
  311.             stub = [ruledScrollView bottomRightStub];
  312.         if ((leftOrBottomRuler) && (!stub) && ([ruledScrollView primaryRulers] == LEFTRIGHT)) {
  313.             [leftOrBottomRuler getFrame:&rulerFrame];
  314.             leftOrBottomMargin = rulerFrame.size.height;
  315.             }
  316.  
  317.         rightOrTopRuler = [ruledScrollView topRuler];
  318.         if ([ruledScrollView leftRuler] == self)
  319.             stub = [ruledScrollView topLeftStub];
  320.         else
  321.             stub = [ruledScrollView topRightStub];
  322.         if ((rightOrTopRuler) && (!stub) && ([ruledScrollView primaryRulers] == LEFTRIGHT)) {
  323.             [rightOrTopRuler getFrame:&rulerFrame];
  324.             rightOrTopMargin = rulerFrame.size.height;
  325.             }
  326.         theRect.size.height = mainViewFrame.size.height
  327.             + leftOrBottomMargin + rightOrTopMargin;
  328.         }
  329.     [self setFrame:(NXRect *)&theRect];
  330.     return self;
  331. }
  332.  
  333. /* Setting/returning Ruler attributes */
  334. - setScaleSpacing:(NXCoord)points
  335. {
  336.     scaleSpacing = points;
  337.     return self;
  338. }
  339.  
  340. - (NXCoord)scaleSpacing { return scaleSpacing; }
  341.  
  342. - setScale:(float)scaleIncrement
  343.   // the increment of the scale reading between major marks
  344. {
  345.     scale = scaleIncrement;
  346.     return self;
  347. }
  348.  
  349. - (float)scale { return scale; }
  350.  
  351. - setScaleOffset:(float)origin
  352. {
  353.     scaleOffset = origin;
  354.     return self;
  355. }
  356.  
  357. - (float)scaleOffset { return scaleOffset; }
  358.  
  359. - setBorder:(NXCoord)theBorder
  360. {
  361.     border = theBorder;
  362.     return self;
  363. }
  364.  
  365. - (NXCoord)border { return border; }
  366.  
  367. - setOrientation:(float)angle
  368.   // set to the manifest HORIZONTAL or VERTICAL
  369.   // must be set before sending setSize:
  370. {
  371.     orientation = angle;
  372.     return self;
  373. }
  374.  
  375. - (float)orientation { return orientation; }
  376.  
  377. - setInverted:(BOOL)invertFlag
  378.   // a flag that may be checked in drawself: to draw a 
  379.   // ruler reflected vertically top or right side
  380. {
  381.     inverted = invertFlag;
  382.     return self;
  383. }
  384.  
  385. - (BOOL)inverted { return inverted; }
  386.  
  387. - setMirrored:(BOOL)mirrorFlag
  388.   // a flag that may be checked in drawself: to draw a 
  389.   // the ruler reflected horizontally
  390. {
  391.     mirrored = mirrorFlag;
  392.     return self;
  393. }
  394.  
  395. - (BOOL)mirrored { return mirrored; }
  396.  
  397. - setAlignment:(int)alignType
  398.   // set to one of NX_LEFTALIGNED, NX_CENTERED, NX_RIGHTALIGNED
  399.   // manifests defined in appkit/Text.h
  400. {
  401.     alignment = alignType;
  402.     return self;
  403. }
  404.  
  405. - (int)alignment { return alignment; }
  406.  
  407. - setUnits:(char *)name
  408. {
  409.     if (name == NULL) return self;
  410.     if (strlen(units) < strlen(name)) {
  411.         free(units);
  412.         units = malloc(strlen(name)*sizeof(char));
  413.         }
  414.     strcpy(units,name);
  415.     return self;
  416. }    
  417.  
  418. - (const char *)units {return (const char *)units;}
  419.  
  420. - setFont:aFont
  421. {
  422.  
  423.     font = aFont;
  424.     NXTextFontInfo(font, &ascender, &descender, &lineHeight);
  425.     if (descender < 0.0) descender = -1.0 * descender;
  426.  
  427.     return self;
  428. }
  429.  
  430. - (Font *)font { return font; }
  431.  
  432. - setBackgroundColor:(NXColor)bColor
  433. {
  434.     backgroundColor = bColor;
  435.     return self;
  436. }
  437.  
  438. - (NXColor)backgroundColor {return backgroundColor;}
  439.  
  440. - setBackgroundGray:(float)value;
  441. {
  442.     backgroundGray = value;
  443.     return self;
  444. }
  445.  
  446. - (float)backgroundGray{return backgroundGray;}
  447.  
  448. - setImage:anImage
  449. {
  450.     /* an image object for rulers that tile an image to the background */
  451.     image = anImage;
  452.     return self;
  453. }
  454.  
  455. - image
  456. {
  457.     return image;
  458. }
  459.  
  460. - setDraggableRuler:(BOOL)yesOrNo;
  461. {
  462.     draggableRuler = yesOrNo;
  463.     return self;
  464. }
  465.  
  466. - (BOOL)draggableRuler {return draggableRuler; }
  467.  
  468. @end
  469.