Rev 1558 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
/** R.app : a Cocoa front end to: "R A Computer Language for Statistical Data Analysis"** R.app Copyright notes:* Copyright (C) 2004-5 The R Foundation* written by Stefano M. Iacus and Simon Urbanek*** R Copyright notes:* Copyright (C) 1995-1996 Robert Gentleman and Ross Ihaka* Copyright (C) 1998-2001 The R Development Core Team* Copyright (C) 2002-2004 The R Foundation** This program is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License as published by* the Free Software Foundation; either version 2 of the License, or* (at your option) any later version.** This program is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the* GNU General Public License for more details.** A copy of the GNU General Public License is available via WWW at* http://www.gnu.org/copyleft/gpl.html. You can also obtain it by* writing to the Free Software Foundation, Inc., 59 Temple Place,* Suite 330, Boston, MA 02111-1307 USA.*//* QuartzDevice.m */#import "../RGUI.h"#import "../REngine/RSEXP.h"#import "../REngine/REngine.h"#include <Rversion.h>#include <R.h>#include <R_ext/Boolean.h>#include <R_ext/Rdynload.h>#include <Rdefines.h>#include <Rinternals.h>#include <R_ext/Parse.h>#import "../RController.h"#import "RQuartz.h"#import "RDeviceView.h"#import "QuartzDevice.h"#import "Preferences.h"#include <Graphics.h>#include <Rdevices.h>/* since 2.1.0 devQuartz is no longer in libR, so we need to duplicate it here */#if (R_VERSION >= R_Version(2,1,0))extern char* RGUI_Quartz_TranslateFontFamily(char* family, int face, char *devfamily);#define Quartz_TranslateFontFamily RGUI_Quartz_TranslateFontFamily#elseextern char* Quartz_TranslateFontFamily(char* family, int face, char *devfamily); /* from src/unix/devQuartz.c */#endifNSRect ClipArea;/***************************************************************************//* Each driver can have its own device-specic graphical *//* parameters and resources. these should be wrapped *//* in a structure (like the x11Desc structure below) *//* and attached to the overall device description via *//* the dd->deviceSpecific pointer *//* NOTE that there are generic graphical parameters *//* which must be set by the device driver, but are *//* common to all device types (see Graphics.h) *//* so go in the GPar structure rather than this device- *//* specific structure *//***************************************************************************/typedef struct {int cex;int windowWidth;int windowHeight;Boolean resize;int Text_Font; /* 0 is system font and 4 is monaco */int fontface; /* Typeface */int fontsize; /* Size in points */int usefixed;int color; /* color */int bg; /* bg color */int fill; /* fill color */WindowPtr window;int lineType;int lineWidth;Boolean Antialias; /* Use Antialiasing */Boolean Autorefresh;char *family;CGContextRef context; /* This is the context used by Quartz for OnScreen drawings */CGContextRef auxcontext; /* Additional context used for: cliboard, printer, file */double xscale;double yscale;int DevNum;int where;int QuartzPos; /* Window Pos: TopRight=1, BottomRight, BottomLeft, TopLeft=4 */RQuartz *QuartzDoc;NSPoint topLeftPoint;RDeviceView *DevView;NSWindow *DevWindow;NSTextStorage *DevTextStorage;NSLayoutManager *DevLayoutManager;NSTextContainer *DevTextContainer;}QuartzDesc;Rboolean innerQuartzDevice(NewDevDesc *dd,char *display,double width,double height,double pointsize,char *family,Rboolean antialias,Rboolean autorefresh,int quartzpos,int bg);void getQuartzParameters(double *width, double *height, double *ps, char *family,Rboolean *antialias, Rboolean *autorefresh, int *quartzpos);static Rboolean RQuartz_Open(NewDevDesc *, QuartzDesc *, char *,double, double, int);static void RQuartz_Close(NewDevDesc *dd);static void RQuartz_Activate(NewDevDesc *dd);static void RQuartz_Deactivate(NewDevDesc *dd);static void RQuartz_Size(double *left, double *right,double *bottom, double *top, NewDevDesc *dd);static void RQuartz_NewPage(R_GE_gcontext *gc, NewDevDesc *dd);static void RQuartz_Clip(double x0, double x1, double y0, double y1,NewDevDesc *dd);static double RQuartz_StrWidth(char *str,R_GE_gcontext *gc,NewDevDesc *dd);static void RQuartz_Text(double x, double y, char *str,double rot, double hadj,R_GE_gcontext *gc,NewDevDesc *dd);static void RQuartz_Rect(double x0, double y0, double x1, double y1,R_GE_gcontext *gc,NewDevDesc *dd);static void RQuartz_Circle(double x, double y, double r,R_GE_gcontext *gc,NewDevDesc *dd);static void RQuartz_Line(double x1, double y1, double x2, double y2,R_GE_gcontext *gc,NewDevDesc *dd);static void RQuartz_Polyline(int n, double *x, double *y,R_GE_gcontext *gc,NewDevDesc *dd);static void RQuartz_Polygon(int n, double *x, double *y,R_GE_gcontext *gc,NewDevDesc *dd);static Rboolean RQuartz_Locator(double *x, double *y, NewDevDesc *dd);static void RQuartz_Mode(int mode, NewDevDesc *dd);static void RQuartz_Hold(NewDevDesc *dd);static void RQuartz_MetricInfo(int c,R_GE_gcontext *gc,double* ascent, double* descent,double* width,NewDevDesc *dd);static void RQuartz_SetFill(int fill, double gamma, NewDevDesc *dd);static void RQuartz_SetStroke(int color, double gamma, NewDevDesc *dd);static void RQuartz_SetLineProperties(R_GE_gcontext *gc, NSBezierPath *path, NewDevDesc *dd);static void RQuartz_SetLineDash(int lty, double lwd, NSBezierPath *path, NewDevDesc *dd);static void RQuartz_SetLineWidth(double lwd, NSBezierPath *path);static void RQuartz_SetLineEnd(R_GE_lineend lend, NSBezierPath *path);static void RQuartz_SetLineJoin(R_GE_linejoin ljoin, NSBezierPath *path);static void RQuartz_SetLineMitre(double lmitre, NSBezierPath *path);NSFont *RQuartz_Font(R_GE_gcontext *gc, NewDevDesc *dd);NSPoint computeTopLeftCornerForDevNum(int devnum, int quartzpos, int width, int height);Rboolean innerQuartzDevice(NewDevDesc*dd,char*display,double width,double height,double pointsize,char*family,Rboolean antialias,Rboolean autorefresh,int quartzpos,int bg){// NSLog(@"innerQuartzDevice called");BOOL flag=[Preferences flagForKey:useQuartzPrefPaneSettingsKey withDefault: NO];if (flag) {NSString *valw = [Preferences stringForKey:quartzPrefPaneWidthKey withDefault: @"4.5"];NSString *valh = [Preferences stringForKey:quartzPrefPaneHeightKey withDefault: @"4.5"];width = [valw doubleValue];height = [valh doubleValue];}// NSLog(@"quartzpos value: %d", quartzpos);quartzpos = [[Preferences stringForKey:quartzPrefPaneLocationIntKey withDefault:@"3"] intValue];// NSLog(@"Pref quartzpos value: %d", quartzpos);QuartzDesc *xd;int ps;if (!(xd = (QuartzDesc *)malloc(sizeof(QuartzDesc))))return 0;xd->QuartzPos = quartzpos; /* by default it is Top-Right */if(!RQuartz_Open(dd, xd, display, width, height, bg))return(FALSE);ps = pointsize;if (ps < 6 || ps > 24) ps = 10;ps = 2 * (ps / 2);dd->startps = ps;dd->startfont = 1;dd->startlty = LTY_SOLID;dd->startgamma = 1;dd->newDevStruct = 1;dd->open = RQuartz_Open;dd->close = RQuartz_Close;dd->activate = RQuartz_Activate;dd->deactivate = RQuartz_Deactivate;dd->size = RQuartz_Size;dd->newPage = RQuartz_NewPage;dd->clip = RQuartz_Clip;dd->strWidth = RQuartz_StrWidth;dd->text = RQuartz_Text;dd->rect = RQuartz_Rect;dd->circle = RQuartz_Circle;dd->line = RQuartz_Line;dd->polyline = RQuartz_Polyline;dd->polygon = RQuartz_Polygon;dd->locator = RQuartz_Locator;dd->mode = RQuartz_Mode;dd->hold = RQuartz_Hold;dd->metricInfo = RQuartz_MetricInfo;dd->left = 0;dd->right = xd->windowWidth;dd->bottom = xd->windowHeight;dd->top = 0;dd->xCharOffset = 0.4900;dd->yCharOffset = 0.3333;dd->yLineBias = 0.1;dd->cra[0] = ps / 2;dd->cra[1] = ps;dd->ipr[0] = 1.0 / 72;dd->ipr[1] = 1.0 / 72;dd->canResizePlot = TRUE;dd->canChangeFont = TRUE;dd->canRotateText = TRUE;dd->canResizeText = TRUE;dd->canClip = TRUE;dd->canHAdj = 0;dd->canChangeGamma = FALSE;/* It is used to set the font that you will be used on the postscript anddrawing.*//* There is the place for you to set the default value of the MAC Devices */xd->cex = 1.0;xd->resize = true;xd->Text_Font = 4; /* initial is monaco */xd->fontface = 0; /* initial is plain text */xd->fontsize = 12; /* initial is 12 size */xd->Antialias = antialias; /* by default Antialias if on */xd->Autorefresh = autorefresh; /* by default it is on */if(family){xd->family = malloc(sizeof(family)+1);strcpy(xd->family,family);}elsexd->family = NULL;xd->where = kOnScreen;//err = SetCGContext(xd);/* This scale factor is needed in MetricInfo */xd->xscale = width/72.0;xd->yscale = height/72.0;dd->deviceSpecific = (void *) xd;dd->displayListOn = TRUE;xd->DevView = [xd->QuartzDoc getDeviceView];xd->DevWindow = [xd->QuartzDoc getDeviceWindow];[xd->DevView setDevNum: xd->DevNum];[xd->DevView setPDFDrawing: FALSE];xd->topLeftPoint = NSMakePoint(0, 0);return 1;}void getQuartzParameters(double *width, double *height, double *ps, char *family,Rboolean *antialias, Rboolean *autorefresh, int *quartzpos){}void RQuartz_DiplayGList(RDeviceView * devView);void RQuartz_DiplayGList(RDeviceView * devView){NewDevDesc *dd;int devnum = [devView getDevNum];if( (dd = ((GEDevDesc*) GetDevice(devnum))->dev) ){QuartzDesc *xd = (QuartzDesc *) dd-> deviceSpecific;NSSize size = [devView bounds].size;if( (xd->windowWidth != size.width) || (xd->windowHeight != size.height) || ([devView isPDFDrawing])){xd->resize = true;dd->size(&(dd->left), &(dd->right), &(dd->bottom), &(dd->top), dd);xd->resize = false;GEplayDisplayList((GEDevDesc*) GetDevice(devnum));}}}static Rboolean RQuartz_Open(NewDevDesc *dd, QuartzDesc *xd, char *dsp,double wid, double hgt, int bg){// NSLog(@"RQuartz_Open called");RQuartz *newDocument;xd->windowWidth = wid*72;xd->windowHeight = hgt*72;xd->window = NULL;xd->context = NULL;xd->auxcontext = NULL;xd->bg = dd->startfill = bg; /* 0xffffffff; transparent */dd->startcol = R_RGB(0, 0, 0);/* Create a new window with the specified size */newDocument = [[NSDocumentController sharedDocumentController]openUntitledDocumentOfType:@"pdf" display:NO];if(newDocument == nil){NSLog(@"Could not create new quartz device document");return 0;}[RQuartz changeDocumentTitle: newDocument Title:NLS(@"New Quartz Device")];xd->topLeftPoint = computeTopLeftCornerForDevNum(0, xd->QuartzPos, xd->windowWidth, xd->windowHeight);// NSLog(@"topLeftPoint: %f %f", xd->topLeftPoint.x, xd->topLeftPoint.y);[[newDocument getDeviceWindow] setFrameOrigin:NSMakePoint(xd->topLeftPoint.x, xd->topLeftPoint.y - xd->windowHeight)];// NSRect wrect;// wrect = [[newDocument getDeviceWindow] frame];// NSLog(@"frame: %f %f", wrect.origin.x, wrect.origin.y);[[newDocument getDeviceWindow] setContentSize:NSMakeSize(xd->windowWidth, xd->windowHeight) ];[[newDocument getDeviceWindow] orderFrontRegardless];xd->QuartzDoc = newDocument;xd->color = xd->fill = R_TRANWHITE;xd->resize = false;xd->lineType = 0;xd->lineWidth = 1;return TRUE;}static void RQuartz_Close(NewDevDesc *dd){QuartzDesc *xd = (QuartzDesc *) dd->deviceSpecific;if(xd->QuartzDoc)[xd->QuartzDoc close];if(xd->family)free(xd->family);free(xd);}static void RQuartz_Activate(NewDevDesc *dd){// NSLog(@"RQuartz_Activate called");int devnum = devNumber((DevDesc *)dd);QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;xd->DevNum = devnum;[xd->DevView setDevNum: devnum];// NSLog(@"Devnum: %d", devnum);NSRect rect = [xd->DevWindow frame];NSScreen *screen = [NSScreen mainScreen];NSDictionary *screenDict = [screen deviceDescription];NSSize resolution = [[screenDict objectForKey:NSDeviceResolution] sizeValue];// NSSize screenSize = [[screenDict objectForKey:NSDeviceSize] sizeValue];// NSLog(@"Resolution: %f %f", resolution.width, resolution.height);// NSLog(@"Screen size: %f %f", screenSize.width, screenSize.height);float width = [[Preferences stringForKey:quartzPrefPaneWidthKey withDefault:@"4.5"] floatValue];float height = [[Preferences stringForKey:quartzPrefPaneHeightKey withDefault:@"4.5"] floatValue];float fwidth = width * resolution.width;float fheight = height * resolution.height + 22.0;/*Not great. Would have preferred to set window position in RQuartz_Open but cannot as devnumis not known at that point in time. Here I move the window to the right location the 1st timearound and if it has not been moved or resized. xd->topLeftPoint contains the tLP coordinatesas computed by computeTopLeftCornerForDevNum(devnum-1) and rect the actuals.Note also that after resizing, updatePreferences is called in RQuartz.m.Unfortunately the needed call to setContentSize in RQuartz_Open generates the initial flickeron opening a new window. Have not found a way around that (e.g. setFrame:display does not work).*/if (xd->topLeftPoint.x < 5.0 ||fwidth==rect.size.width && fheight==rect.size.height &&xd->topLeftPoint.x == rect.origin.x && xd->topLeftPoint.y == rect.origin.y + fheight) {xd->topLeftPoint = computeTopLeftCornerForDevNum(devnum-1, xd->QuartzPos, xd->windowWidth, xd->windowHeight);[xd->DevWindow setFrameTopLeftPoint:xd->topLeftPoint];}[RQuartz changeDocumentTitle: xd->QuartzDoc Title:[NSString stringWithFormat:NLS(@"Quartz (%d) - Active"),devnum+1]];}static void RQuartz_Deactivate(NewDevDesc *dd){int devnum = devNumber((DevDesc *)dd);QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;[RQuartz changeDocumentTitle: xd->QuartzDoc Title:[NSString stringWithFormat:NLS(@"Quartz (%d) - Inactive"),devnum+1]];}static void RQuartz_Size(double *left, double *right,double *bottom, double *top, NewDevDesc *dd){QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;NSSize size = [xd->DevView bounds].size;*top = 0.0;*left = 0.0;*right = size.width;*bottom = size.height;if(xd->resize){xd->windowWidth = size.width;xd->windowHeight = size.height;xd->resize = false;}return;}static void RQuartz_NewPage(R_GE_gcontext *gc, NewDevDesc *dd){QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;if ([xd->DevWindow isMiniaturized])[xd->DevWindow deminiaturize:nil];[xd->DevView lockFocus];ClipArea = [xd->DevView bounds];NSRectClip(ClipArea);if(R_OPAQUE(gc->fill))RQuartz_SetFill(gc->fill, gc->gamma, dd);else[[NSColor windowBackgroundColor] set];NSRectFill( ClipArea );[xd->DevView unlockFocus];}/* This function only sets the clipping area (CliapArea) to clipnext time drawing occurs*/static void RQuartz_Clip(double x0, double x1, double y0, double y1,NewDevDesc *dd){float x, y, width, height;if (x0 < x1) {x = x0;width = (float)(x1 -x0);}else {x = x1;width = (float)(x0 -x1);}if (y0 < y1) {y = y0;height = (float)(y1 -y0);}else {y = y1;height = (float)(y0-y1);}ClipArea = NSMakeRect(x,y,width,height);}static double RQuartz_StrWidth(char *str,R_GE_gcontext *gc,NewDevDesc *dd){NSString *text;if(gc->fontface==5 || strcmp(gc->fontfamily,"symbol")==0)text = [[NSString alloc] initWithBytes:str length:strlen(str) encoding:NSSymbolStringEncoding];elsetext = [[NSString stringWithUTF8String:str] retain];if (!text) return 0.0;NSDictionary* attr = [NSDictionary dictionaryWithObjectsAndKeys:RQuartz_Font(gc, dd), NSFontAttributeName,NULL];NSSize bbox = [text sizeWithAttributes:attr];[text release];return bbox.width;}static void RQuartz_Text(double x, double y, char *str,double rot, double hadj,R_GE_gcontext *gc,NewDevDesc *dd){NSString *text = nil;QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;if(gc->fontface==5 || strcmp(gc->fontfamily,"symbol")==0)text = [[NSString alloc] initWithBytes:str length:strlen(str) encoding:NSSymbolStringEncoding];elsetext = [[NSString stringWithUTF8String:str] retain];if (!text) return; /* if for some reason the decoding didn't work, just get out, nothing to see */NSColor* clr = [NSColor colorWithCalibratedRed:(float)R_RED(gc->col)/255.0green:(float)R_GREEN(gc->col)/255.0blue:(float)R_BLUE(gc->col)/255.0alpha:(float)R_ALPHA(gc->col)/255.0];NSFont *font = RQuartz_Font(gc, dd);NSDictionary* attr = [NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName,clr, NSForegroundColorAttributeName,nil];[xd->DevView lockFocus];NSRectClip(ClipArea);//NSSize bbox = [text sizeWithAttributes:attr];NSAffineTransform *tns = [NSAffineTransform transform];[tns translateXBy:x yBy:y];[tns rotateByDegrees:-rot];// in theory it should be sufficient to shift by the ascender, but obviously R is providing the wrong coordinates ...[tns translateXBy:0 yBy:-[font ascender]+[font descender]];//[tns translateXBy:0 yBy:-bbox.height];[tns concat];[text drawAtPoint:NSMakePoint(0,0) withAttributes:attr];[text release];[xd->DevView unlockFocus];}static void RQuartz_Rect(double x0, double y0, double x1, double y1,R_GE_gcontext *gc,NewDevDesc *dd){QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;NSBezierPath *rPath = [NSBezierPath bezierPath];[rPath appendBezierPathWithRect:NSMakeRect(x0,y0,x1-x0,y1-y0)];[xd->DevView lockFocus];NSRectClip(ClipArea);RQuartz_SetStroke( gc->col, gc->gamma, dd);RQuartz_SetFill( gc->fill, gc->gamma, dd);RQuartz_SetLineProperties(gc, rPath, dd);[rPath fill];[rPath stroke];[xd->DevView unlockFocus];}static void RQuartz_Circle(double x, double y, double r,R_GE_gcontext *gc,NewDevDesc *dd){QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;NSBezierPath *rPath = [NSBezierPath bezierPath];[rPath appendBezierPathWithOvalInRect:NSMakeRect(x-r, y-r, 2*r, 2*r)];[xd->DevView lockFocus];NSRectClip(ClipArea);RQuartz_SetStroke( gc->col, gc->gamma, dd);RQuartz_SetFill( gc->fill, gc->gamma, dd);RQuartz_SetLineProperties(gc, rPath, dd);[rPath fill];[rPath stroke];[xd->DevView unlockFocus];}static void RQuartz_Line(double x1, double y1, double x2, double y2,R_GE_gcontext *gc,NewDevDesc *dd){QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;NSBezierPath *rPath = [NSBezierPath bezierPath];[rPath moveToPoint:NSMakePoint(x1,y1)];[rPath lineToPoint:NSMakePoint(x2,y2)];[xd->DevView lockFocus];NSRectClip(ClipArea);RQuartz_SetStroke( gc->col, gc->gamma, dd);RQuartz_SetFill( gc->fill, gc->gamma, dd);RQuartz_SetLineProperties(gc, rPath, dd);[rPath stroke];[xd->DevView unlockFocus];}static void RQuartz_Polyline(int n, double *x, double *y,R_GE_gcontext *gc,NewDevDesc *dd){int i;QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;NSBezierPath *rPath = [NSBezierPath bezierPath];[rPath moveToPoint:NSMakePoint(x[0],y[0])];for (i = 1; i < n; i++)[rPath lineToPoint:NSMakePoint(x[i],y[i])];[xd->DevView lockFocus];NSRectClip(ClipArea);RQuartz_SetStroke( gc->col, gc->gamma, dd);RQuartz_SetFill( gc->fill, gc->gamma, dd);RQuartz_SetLineProperties(gc, rPath, dd);[rPath stroke];[xd->DevView unlockFocus];}static void RQuartz_Polygon(int n, double *x, double *y,R_GE_gcontext *gc,NewDevDesc *dd){int i;QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;NSBezierPath *rPath = [NSBezierPath bezierPath];[rPath moveToPoint:NSMakePoint(x[0],y[0])];for (i = 1; i < n; i++)[rPath lineToPoint:NSMakePoint(x[i],y[i])];[rPath lineToPoint:NSMakePoint(x[0],y[0])];[rPath closePath];[xd->DevView lockFocus];NSRectClip(ClipArea);RQuartz_SetStroke( gc->col, gc->gamma, dd);RQuartz_SetFill( gc->fill, gc->gamma, dd);RQuartz_SetLineProperties(gc, rPath, dd);[rPath fill];[rPath stroke];[xd->DevView unlockFocus];}static void RQuartz_Mode(int mode, NewDevDesc *dd){QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;if(mode==0)[xd->DevWindow flushWindow];}static void RQuartz_SetFill(int fill, double gamma, NewDevDesc *dd){QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;xd->fill = fill;[[NSColor colorWithCalibratedRed:(float)R_RED(fill)/255.0green:(float)R_GREEN(fill)/255.0blue:(float)R_BLUE(fill)/255.0alpha:(float)R_ALPHA(fill)/255.0] setFill];}static void RQuartz_SetStroke(int color, double gamma, NewDevDesc *dd){QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;xd->color = color;[[NSColor colorWithCalibratedRed:(float)R_RED(color)/255.0green:(float)R_GREEN(color)/255.0blue:(float)R_BLUE(color)/255.0alpha:(float)R_ALPHA(color)/255.0] setStroke];}static void RQuartz_SetLineProperties(R_GE_gcontext *gc, NSBezierPath *path, NewDevDesc *dd){RQuartz_SetLineWidth(gc->lwd, path);RQuartz_SetLineDash(gc->lty, gc->lwd, path, dd);RQuartz_SetLineEnd(gc->lend, path);RQuartz_SetLineJoin(gc->ljoin, path);RQuartz_SetLineMitre(gc->lmitre, path);}static void RQuartz_SetLineDash(int newlty, double lwd, NSBezierPath *path, NewDevDesc *dd){QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;float dashlist[8];int i, ndash = 0;lwd *= 0.75; /* kludge from postscript/pdf */for(i = 0; i < 8 && newlty & 15 ; i++) {dashlist[ndash++] = (lwd >= 1 ? lwd: 1) * (newlty & 15);newlty = newlty >> 4;}xd->lineType = newlty;[path setLineDash: dashlist count:ndash phase:0.0];}static void RQuartz_SetLineWidth(double lwd, NSBezierPath *path){[path setLineWidth: lwd];}static void RQuartz_SetLineEnd(R_GE_lineend lend, NSBezierPath *path){NSLineCapStyle linecap=nil;switch (lend) {case GE_ROUND_CAP:linecap = NSRoundLineCapStyle;break;case GE_BUTT_CAP:linecap = NSButtLineCapStyle;break;case GE_SQUARE_CAP:linecap = NSSquareLineCapStyle;break;default:NSLog(@"RQuartz_SetLineEnd: Invalid line end");break;}if (linecap)[path setLineCapStyle:linecap];}static void RQuartz_SetLineJoin(R_GE_linejoin ljoin, NSBezierPath *path){NSLineJoinStyle linejoin=nil;switch (ljoin) {case GE_ROUND_JOIN:linejoin = NSRoundLineJoinStyle;break;case GE_MITRE_JOIN:linejoin = NSMiterLineJoinStyle;break;case GE_BEVEL_JOIN:linejoin = NSBevelLineJoinStyle;break;default:NSLog(@"RQuartz_SetLineJoin: Invalid line join");break;}if (linejoin)[path setLineJoinStyle: linejoin];}static void RQuartz_SetLineMitre(double lmitre, NSBezierPath *path){if (lmitre < 1)NSLog(@"RQuartz_SetLineMitre:Invalid line mitre");else[path setMiterLimit: lmitre];}NSFont *RQuartz_Font(R_GE_gcontext *gc, NewDevDesc *dd){NSFontManager *fm = [NSFontManager sharedFontManager];QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;int size = gc->cex*gc->ps + 0.5;char *fontFamily=0;NSFontTraitMask traits = 0;NSMutableString *CurrFont = [[NSMutableString alloc] initWithCapacity:1];if((gc->fontface == 5) || (strcmp(gc->fontfamily,"symbol")==0))[CurrFont setString: @"Symbol"];else {fontFamily = Quartz_TranslateFontFamily(gc->fontfamily, gc->fontface,xd->family);if (fontFamily)[CurrFont setString: [NSString stringWithCString:fontFamily]];else[CurrFont setString: @"Helvetica"];}if( ![CurrFont isEqual: @"Symbol"] )traits=((gc->fontface==2||gc->fontface==4)?NSBoldFontMask:0)|((gc->fontface==3||gc->fontface==4)?NSItalicFontMask:0);NSRange range = NSMakeRange(0, [CurrFont length]);range = [CurrFont rangeOfString: @"-" options: 0 range: range];if (range.location != NSNotFound )[CurrFont setString: [CurrFont substringWithRange:NSMakeRange(0, range.location)]];NSFont* font = [fm fontWithFamily:CurrFont traits:traits weight:5 size:size];return font;}static void RQuartz_Hold(NewDevDesc *dd){}/* FIXME: the metric info is not correct, in particular,it is somewhat bigger than it should be.*/static void RQuartz_MetricInfo(int c,R_GE_gcontext *gc,double* ascent, double* descent,double* width,NewDevDesc *dd){//QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;NSFont* font= RQuartz_Font(gc,dd);unichar uc = (unichar)c;char str[2];str[1] = '\0';str[0] = (char)c;*ascent = [font ascender];*descent = [font descender];*width = [font maximumAdvancement].width;if (c) {NSDictionary* attr = [NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName,nil];NSString *text = nil;if(gc->fontface==5 ||strcmp(gc->fontfamily,"symbol")==0)text = [[NSString alloc] initWithBytes:str length:1 encoding:NSSymbolStringEncoding];elsetext = [[NSString stringWithCharacters:&uc length:1] retain];*width = [text sizeWithAttributes: attr].width;[text release];}//fprintf(stderr,"c=%c,ascent=%f, descent=%f,width=%f,width2=%f,width3=%f\n",c,*ascent , *descent, *width, *width+rect2.origin.x,NSWidth(rect));// NSLog(@"x=%f,y=%f,width=%f,height=%f",rect.origin.x, rect.origin.y,rect.size.width,rect.size.height);// NSLog(@"x=%f,y=%f,width=%f,height=%f",rect2.origin.x, rect2.origin.y,rect2.size.width,rect2.size.height);}/* Adapted from original code by: Byron Ellys, StatPaperBundle, DeviceController: locatorForX, cocoaLocator*/static Rboolean RQuartz_Locator(double *x,double *y,NewDevDesc*dd){QuartzDesc *xd = (QuartzDesc*)dd->deviceSpecific;[xd->DevWindow makeKeyAndOrderFront:xd->DevWindow];int useBeep = asLogical(GetOption(install("locatorBell"),R_NilValue));for(;;) {NSEvent*event = [xd->DevWindow nextEventMatchingMask:NSEventMaskFromType(NSLeftMouseDown)|NSEventMaskFromType(NSKeyDown)];NSPoint p;if([event window] == xd->DevWindow) {switch([event type]) {case NSLeftMouseDown:p = [xd->DevView convertPoint:[event locationInWindow] fromView:nil];// NSLog(@"Location: %.2f,%.2f",p.x,p.y);*x = p.x; *y = p.y;if(useBeep)NSBeep();return 1;break;case NSKeyDown:return 0;break;default:// don't bark - it may be other button thus the action may be ok//NSLog(@"Unknown event from locator");return 0;break;}}}}NSPoint computeTopLeftCornerForDevNum(int devnum, int quartzpos, int width, int height) {NSPoint topLeftCorner;NSScreen *screen = [NSScreen mainScreen];NSDictionary *screenDict = [screen deviceDescription];NSSize resolution = [[screenDict objectForKey:NSDeviceResolution] sizeValue];NSSize screenSize = [[screenDict objectForKey:NSDeviceSize] sizeValue];// NSLog(@"Resolution: %f %f", resolution.width, resolution.height);// NSLog(@"Screen size: %f %f", screenSize.width, screenSize.height);// int quartzpos = [[Preferences stringForKey:quartzPrefPaneLocationIntKey withDefault:@"3"] intValue];// int width = [[Preferences stringForKey:quartzPrefPaneWidthKey withDefault:@"4.5"] intValue];// int height = [[Preferences stringForKey:quartzPrefPaneHeightKey withDefault:@"4.5"] intValue];float x, y;switch(quartzpos){case 0: // Top rightx = screenSize.width - (width + resolution.width);y = screenSize.height - 0.5 * resolution.height;break;case 1: // Bottom rightx = screenSize.width - (width + resolution.width);y = height + resolution.height;break;case 2: // Bottom leftx = 0.5 * resolution.width;y = height + resolution.height;break;case 3: // Top leftx = 0.5 * resolution.width;y = screenSize.height - 0.5 * resolution.height;break;case 4: // Centeredx = screenSize.width/2 - width/2;y = screenSize.height/2 + height/2;break;default:x = 0.5 * resolution.width;y = screenSize.height - 0.5 * resolution.height;break;}topLeftCorner.x = x + devnum * 21;topLeftCorner.y = y - devnum * 23;// NSLog(@"Screen at: %f %f with devnum=%d, pos=%d, width=%d and height=%d", topLeftCorner.x, topLeftCorner.y, devnum, quartzpos, width, height);return topLeftCorner;}