Rev 6192 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
/** R : A Computer Langage for Statistical Data Analysis* Copyright (C) 1995, 1996 Robert Gentleman and Ross Ihaka** 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.** You should have received a copy of the GNU General Public License* along with this program; if not, write to the Free Software* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA*/#ifdef HAVE_CONFIG_H#include <Rconfig.h>#endif#include "Defn.h"#include "Print.h"#if defined(Macintosh) | defined(Unix)#include "dataentry.h"#ifdef Macintosh#include "MACconsole.h"#endif/*The spreadsheet function returns a list of vectors. The types of thesevectors can be specified by the user as can their names. It the namesare specified they are set during initialization. The user can changethese via a menu interface, they can also change the type.The vectors are created too long and if they need to be increased thisis done by using the next higher power of 2. They start 100 long. To cutthem to the correct length for return you need to know the largest row numberthat was assigned to. LEVELS (sxpinfo.gp) is used to keep track of this,separately for each vector. Vectors are initialized to NA when they arecreated so that NA is returned for any cell that was not set by the user.So that coercion back and forth maintains values of ssNA_REAL and ssNA_STRINGI have set ssNA_STRING to be coerceVector(ssNA_REAL), very weird but easy.In Macintosh we need to call the main event loop to get events. This ensuresthat the spreadsheet interacts well with the other windows. Under X windowswe let the window manager handle those sorts of details.*/#ifndef Macintoshstatic char *menu_label[] ={"Real","Character","Change Name",};#endif/*ssNewVector is just an interface to allocVector but it lets usset the fields to NA. We need to have a special NA for reals andstrings so that we can differentiate between uninitialized elementsin the vectors and user supplied NA's; hence ssNA_REAL and ssNA_STRING*/SEXP ssNewVector(SEXPTYPE type, int vlen){SEXP tvec;int j;tvec = allocVector(type, vlen);for (j = 0; j < vlen; j++)if (type == REALSXP)REAL(tvec)[j] = ssNA_REAL;else if (type == STRSXP)STRING(tvec)[j] = STRING(ssNA_STRING)[0];LEVELS(tvec) = 0;return (tvec);}SEXP do_dataentry(SEXP call, SEXP op, SEXP args, SEXP rho){SEXP tvec2, tvec, colmodes, indata;SEXPTYPE type;int i, j,len, nprotect;RCNTXT cntxt;nprotect = 0;/* count the PROTECT()s */PROTECT(indata = VectorToPairList(CAR(args))); nprotect++;PROTECT(colmodes = VectorToPairList(CADR(args))); nprotect++;if (!isList(indata) || !isList(colmodes))errorcall(call, "invalid argument");/* initialize the constants */bufp = buf;ne = 0;currentexp = 0;nneg = 0;ndecimal = 0;clength = 0;ccol = 1;crow = 1;colmin = 1;rowmin = 1;ssNA_REAL = -NA_REAL;tvec = allocVector(REALSXP, 1);REAL(tvec)[0] = ssNA_REAL;PROTECT(ssNA_STRING = coerceVector(tvec, STRSXP)); nprotect++;#ifdef Macintoshbwidth = hwidth = 0;doneSpread = 1;text_offset = 2;#elsebwidth = 5;hwidth = 30;#endif/* setup inputlist */if (indata != R_NilValue) {PROTECT(inputlist = duplicate(indata)); nprotect++;tvec2 = colmodes;for (tvec = inputlist; tvec != R_NilValue; tvec = CDR(tvec)) {type = TYPEOF(CAR(tvec));if (CAR(tvec2) != R_NilValue)type = str2type(CHAR(STRING(CAR(colmodes))[0]));if (type != STRSXP)type = REALSXP;if (CAR(tvec) == R_NilValue) {if (type == NILSXP)type = REALSXP;CAR(tvec) = ssNewVector(type, 100);TAG(tvec) = install("var1");LEVELS(CAR(tvec)) = 0;}else if (!isVector(CAR(tvec)))errorcall(call, "invalid type for value");else {if (TYPEOF(CAR(tvec)) != type)CAR(tvec) = coerceVector(CAR(tvec), type);LEVELS(CAR(tvec)) = LENGTH(CAR(tvec));}tvec2 = CDR(tvec2);}}else if (colmodes == R_NilValue ) {PROTECT(inputlist = allocList(1)); nprotect++;CAR(inputlist) = ssNewVector(REALSXP, 100);TAG(inputlist) = install("var1");LEVELS(CAR(inputlist)) = 0;}else {errorcall(call, "invalid parameter(s) ");}/* start up the window, more initializing in here */if (initwin())errorcall(call, "invalid device");/* set up a context which will close the window if there is an error */begincontext(&cntxt, 8, R_NilValue, R_NilValue, R_NilValue, R_NilValue);cntxt.cend = &closewin;highlightrect();/*if on the mac call the main event loop */#ifdef Macintoshwhile (doneSpread)EventLoop();#elseeventloop();#endifendcontext(&cntxt);closewin();/* drop out unused columns */i = 0;for (tvec = inputlist; tvec != R_NilValue; tvec = CDR(tvec))if (CAR(tvec) == R_NilValue) {if (i == 0)inputlist = CDR(inputlist);else {tvec2 = nthcdr(inputlist, (i - 1));SETCDR(tvec2, CDR(tvec));}}elsei++;for (tvec = inputlist; tvec != R_NilValue; tvec = CDR(tvec)) {len = LEVELS(CAR(tvec));if (LENGTH(CAR(tvec)) != len) {tvec2 = ssNewVector(TYPEOF(CAR(tvec)), len);PROTECT(tvec);for (j = 0; j < len; j++)if (TYPEOF(CAR(tvec)) == REALSXP) {if (REAL(CAR(tvec))[j] != ssNA_REAL)REAL(tvec2)[j] = REAL(CAR(tvec))[j];elseREAL(tvec2)[j] = NA_REAL;} else if (TYPEOF(CAR(tvec)) == STRSXP) {if (!streql(CHAR(STRING(CAR(tvec))[j]), CHAR(STRING(ssNA_STRING)[0])))STRING(tvec2)[j] = STRING(CAR(tvec))[j];elseSTRING(tvec2)[j] = NA_STRING;} elseerror("spreadsheet: internal memory problem");CAR(tvec) = tvec2;UNPROTECT(1);}}UNPROTECT(nprotect);return PairToVectorList(inputlist);}/* Event Loop Functions */#ifndef Macintoshstatic void eventloop(){int done;DEEvent ioevent;done = 0;while (done == 0) {if (NextEvent(&ioevent)) {switch (WhichEvent(ioevent)) {case activateEvt:drawwindow();break;case mouseDown:done = doMouseDown(&ioevent);break;case keyDown:doSpreadKey(0, &ioevent);break;case MappingNotify:RefreshKeyboardMapping(&ioevent);break;case ConfigureNotify:doConfigure(&ioevent);break;}}}}int doMouseDown(DEEvent * event){return findsquare();}#endifstatic void doSpreadKey(int key, DEEvent * event){KeySym iokey;char text[1];iokey = GetKey(event);text[0] = GetCharP(event);if (CheckControl(event))doControl(event);else if ((iokey == XK_Return) || (iokey == XK_KP_Enter)|| (iokey == XK_Linefeed) || (iokey == XK_Down))advancerect(DOWN);else if (iokey == XK_Left)advancerect(LEFT);else if ((iokey == XK_Right) || (iokey == XK_Tab))advancerect(RIGHT);else if (iokey == XK_Up)advancerect(UP);else if ((iokey == XK_BackSpace) || (iokey == XK_Delete)) {if (clength > 0) {buf[clength - 1] = ' ';printstring(buf, clength, crow, ccol);clength--;bufp--;}elsebell();}else if (iokey == XK_Home)jumpwin(1, 1);else if (IsModifierKey(iokey)) {}elsehandlechar(text);}#ifdef Macintoshstatic void doSpreadActive(DEEvent * myevent, WindowPtr whichWindow,short windowCode){if (windowCode == inContent)findsquare();elsebell();}static void doSpreadInactive(DEEvent * myevent, WindowPtr whichWindow, short windowCode){}static void doSpreadUpdate(WindowPtr theWind){GrafPtr current;GetPort(¤t);SetPort(theWind);BeginUpdate(theWind);drawwindow();EndUpdate(theWind);SetPort(current);}static void doSpreadActivate(int ival){SetPort((WindowPtr) dataentryWindow);SetCursor(&arrow);}static void doSpreadCursor(){}static void doSpreadNotWindow(DEEvent * myevent, WindowPtr whichWindow, short windowCode){if (windowCode == inMenuBar)doSpreadMenu(MenuSelect(myevent->where));else if (windowCode == inDesk)bell();}#endif/* Window Drawing Routines */void drawwindow(){int i;/* if there is an active cell enter the data in it */closerect();/* now set up the window with the new dimensions *//* clearwindow(); */setattribsfromwindow();nwide = (windowWidth - 2 * bwidth) / box_w;setlineattribs(1);for (i = 1; i <= nwide; i++)drawline(i * box_w, hwidth, i * box_w, windowHeight);nhigh = (windowHeight - 2 * bwidth - hwidth) / box_h;for (i = 1; i <= nhigh; i++)drawline(0, hwidth + i * box_h, windowWidth, hwidth + i * box_h);colmax = colmin + (nwide - 2); /* so row 0 and col 0 are reserved for labels */rowmax = rowmin + (nhigh - 2);printlabs();if (inputlist != R_NilValue)for (i = colmin; i <= colmax; i++)drawcol(i);#ifndef Macintosh/* draw the quit box */i = textwidth("Quit", 4);drawrectangle(windowWidth - 6 - bwidth - i, 3, i + 4, hwidth - 6);drawtext(windowWidth - 4 - bwidth - i, hwidth - 5, "Quit", 4);#endif/* set the active rectangle to be the upper left one */crow = 1;ccol = 1;highlightrect();Rsync();}/* find_coords finds the coordinates of the upper left corner of the given square on the screen */void find_coords(int row, int col, int *xcoord, int *ycoord){*xcoord = bwidth + box_w * col;*ycoord = bwidth + hwidth + box_h * row;}/*draw the window with the top left box at column wcol androw wrow*/void jumpwin(int wcol, int wrow){if (wcol < 0 || wrow < 0) {bell();return;}closerect();colmin = wcol;rowmin = wrow;drawwindow();}void advancerect(int which){/* if we are in the header, changing a name then only down is allowed */if (crow < 1 && which != DOWN) {bell();return;}closerect();switch (which) {case UP:if (crow == 1) {if (rowmin == 1)bell();elsejumppage(UP);} elsecrow--;break;case DOWN:if (crow == (nhigh - 1))jumppage(DOWN);elsecrow++;break;case RIGHT:if (ccol == (nwide - 1))jumppage(RIGHT);elseccol++;break;case LEFT:if (ccol == 1) {if (colmin == 1)bell();elsejumppage(LEFT);} elseccol--;break;default:UNIMPLEMENTED("advancerect");}highlightrect();}void drawrow(int whichrow){int i, src_x, src_y, lenip;char rlab[15];SEXP tvec;find_coords(whichrow, 0, &src_x, &src_y);cleararea(src_x, src_y, windowWidth, box_h);setlineattribs(1);for (i = 0; i <= nwide; i++)drawrectangle(i * box_w, src_y, box_w, box_h);sprintf(rlab, "R %d", rowmin + whichrow - 1);printstring(rlab, strlen(rlab), whichrow, 0);lenip = length(inputlist);for (i = colmin; i <= colmax; i++) {if (i > lenip)break;tvec = CAR(nthcdr(inputlist, i - 1));if (tvec != R_NilValue)if (whichrow + rowmin - 1 <= (int)LEVELS(tvec))printelt(tvec, whichrow + rowmin - 2, whichrow, i - colmin + 1);}Rsync();}/*printelt: print the correct value from vector[vrow] into thespread sheet in row ssrow and col sscol*/void printelt(SEXP invec, int vrow, int ssrow, int sscol){char *strp;PrintDefaults(R_NilValue);if (TYPEOF(invec) == REALSXP) {if (REAL(invec)[vrow] != ssNA_REAL) {strp = EncodeElement(invec, vrow, 0);printstring(strp, strlen(strp), ssrow, sscol);}}else if (TYPEOF(invec) == STRSXP) {if (!streql(CHAR(STRING(invec)[vrow]), CHAR(STRING(ssNA_STRING)[0]))) {strp = EncodeElement(invec, vrow, 0);printstring(strp, strlen(strp), ssrow, sscol);}}elseerror("spreadsheet: internal memory error");}void drawcol(int whichcol){int i, src_x, src_y, len;char clab[15];SEXP tmp;find_coords(0, whichcol, &src_x, &src_y);cleararea(src_x, src_y, box_w, windowHeight);setlineattribs(1);for (i = 0; i <= nhigh; i++)drawrectangle(src_x, hwidth + i * box_h, box_w, box_h);/* now fill it in if it is active */if (length(inputlist) >= whichcol + colmin - 1) {tmp = nthcdr(inputlist, whichcol + colmin - 2);if (TAG(tmp) != R_NilValue)printstring(CHAR(PRINTNAME(TAG(tmp))),strlen(CHAR(PRINTNAME(TAG(tmp)))), 0, whichcol);else {sprintf(clab, "var%d", whichcol + colmin - 1);printstring(clab, strlen(clab), 0, whichcol);}if (CAR(tmp) != R_NilValue) {len = ((int)LEVELS(CAR(tmp)) > rowmax) ? rowmax : LEVELS(CAR(tmp));for (i = (rowmin - 1); i < len; i++)printelt(CAR(tmp), i, i - rowmin + 2, whichcol);}}else {sprintf(clab, "var%d", whichcol + colmin - 1);printstring(clab, strlen(clab), 0, whichcol);}Rsync();}void jumppage(int dir){switch (dir) {case UP:rowmin--;rowmax--;copyarea(0, hwidth + box_h, 0, hwidth + 2 * box_h);drawrow(1);break;case DOWN:rowmin++;rowmax++;copyarea(0, hwidth + 2 * box_h, 0, hwidth + box_h);drawrow((nhigh - 1));if (2 * bwidth + box_h * nhigh + hwidth != windowHeight)drawrow(nhigh);break;case LEFT:colmin--;colmax--;copyarea(box_w, hwidth, 2 * box_w, hwidth);drawcol(1);break;case RIGHT:colmin++;colmax++;copyarea(2 * box_w, hwidth, box_w, hwidth);drawcol((nwide - 1));if (2 * bwidth + nwide * box_w != windowWidth)drawcol(nwide);break;}}/* draw a rectangle, used to highlight/downlight the current box */void printrect(int lwd){setlineattribs(lwd);drawrectangle(ccol * box_w, hwidth + crow * box_h, box_w, box_h);Rsync();}void downlightrect(){setforeground(0);printrect(2);setforeground(1);printrect(1);}void highlightrect(){setforeground(1);printrect(2);}/* find out whether the button click was in the quit box */#ifndef Macintoshstatic int checkquit(int xw){int wi;wi = textwidth("Quit", 4);if ((xw < windowWidth - bwidth - 2) && (xw > windowWidth - bwidth - wi - 6))return 1;elsereturn 0;}#endif/*when a buttonpress event happens find the square that is being pointed toif the pointer is in the header we need to see if the quit button waspressed and if so quit. This is done by having findsquare return an intwhich is zero if we should quit and one otherwise*/int findsquare(){int xw, yw, xr, yr, wcol, wrow;closerect();querypointer(&xr, &yr, &xw, &yw);/* check to see if the click was in the header */if (yw < hwidth + bwidth) {if (checkquit(xw))return 1;elsereturn 0;}/* translate to box coordinates */wcol = (xw - bwidth) / box_w;wrow = (yw - bwidth - hwidth) / box_h;/* see if it is in the row labels */if (wcol == 0) {bell();highlightrect();return 0;}/* next check to see if it is in the column labels */if (yw < hwidth + bwidth + box_h) {if (xw > bwidth + box_w)popupmenu(xr, yr, wcol, wrow);else {highlightrect();bell();}} else if (wcol != ccol || wrow != crow) {ccol = wcol;crow = wrow;}highlightrect();return 0;}static SEXP getccol(){SEXP tmp, tmp2;int i, len, wcol, wrow;SEXPTYPE type;char cname[10];wcol = ccol + colmin - 1;wrow = crow + rowmin - 1;if (length(inputlist) < wcol)inputlist = listAppend(inputlist, allocList(wcol - length(inputlist)));tmp = nthcdr(inputlist, wcol - 1);if (CAR(tmp) == R_NilValue) {len = (wrow < 100) ? 100 : wrow;CAR(tmp) = ssNewVector(REALSXP, len);if (TAG(tmp) == R_NilValue) {sprintf(cname, "var%d", wcol);TAG(tmp) = install(cname);}}if (!isVector(CAR(tmp)))error("internal type error in spreadsheet");len = LENGTH(CAR(tmp));type = TYPEOF(CAR(tmp));if (len < wrow) {tmp2 = ssNewVector(type, 2 * len);for (i = 0; i < len; i++)if (type == REALSXP)REAL(tmp2)[i] = REAL(CAR(tmp))[i];else if (type == STRSXP)STRING(tmp2)[i] = STRING(CAR(tmp))[i];elseerror("internal type error in spreadsheet");LEVELS(tmp2) = LEVELS(CAR(tmp));CAR(tmp) = tmp2;}return (CAR(tmp));}/*close up the entry to a square, put the value that has been enteredinto the correct place and as the correct type*/void closerect(){SEXP cvec, tvec;*bufp = '\0';/* first check to see if anything has been entered */if (clength != 0) {if (crow == 0) { /* then we are entering a new column name */if (length(inputlist) < ccol + colmin - 1)inputlist = listAppend(inputlist, allocList((ccol - colmin - 1 + length(inputlist))));tvec = nthcdr(inputlist, ccol + colmin - 2);TAG(tvec) = install(buf);}else {cvec = getccol();if ((crow + rowmin - 1) > (int)LEVELS(cvec))LEVELS(cvec) = (crow + rowmin - 1);if (TYPEOF(cvec) == STRSXP) {tvec = allocString(strlen(buf));strcpy(CHAR(tvec), buf);STRING(cvec)[(rowmin + crow - 2)] = tvec;}elseREAL(cvec)[(rowmin + crow - 2)] = atof(buf);}}else if (crow == 0) {sprintf(buf, "var%d", ccol);printstring(buf, strlen(buf), 0, ccol - colmin + 1);}downlightrect();ndecimal = 0;nneg = 0;ne = 0;currentexp = 0;clength = 0;bufp = buf;}/*print a null terminated string, check to see if it is longer than the print area and printit, left adjusted if necessary; clear the area of previous text;*/void printstring(char *ibuf, int buflen, int row, int col){int len, x_pos, y_pos;find_coords(row, col, &x_pos, &y_pos);cleararea(col * box_w + text_offset, hwidth + row * box_h + text_offset,box_w - 2 * text_offset, box_h - 2 * text_offset);len = nchars(ibuf, buflen);drawtext(x_pos + text_offset, y_pos + box_h - text_offset, ibuf, len);Rsync();}int nchars(char *ibuf, int len){int i;for (i = len; i > 1; i--)if (textwidth(ibuf, i) < (box_w - text_offset))break;return i;}void clearrect(){cleararea(ccol * box_w, hwidth + crow * box_h, box_w, box_h);Rsync();}/*handlechar has to be able to parse decimal numbers and strings,depending on the current column type, only printing characters should get this far*/void handlechar(char *text){int c;SEXP tvec;c = text[0];if (clength == 0) {if (length(inputlist) >= ccol + colmin - 1)tvec = nthcdr(inputlist, ccol + colmin - 2);elsetvec = R_NilValue;if (crow == 0) /* variable name */currentexp = 3;else if (TYPEOF(CAR(tvec)) == STRSXP) /* character data */currentexp = 2;elsecurrentexp = 1; /* numeric data */clearrect();highlightrect();}if (currentexp == 1) /* we are parsing a number */switch (c) {case '-':if (nneg == 0)nneg++;elsegoto donehc;break;case '.':if (ndecimal == 0)ndecimal++;elsegoto donehc;break;case 'e':case 'E':if (ne == 0) {nneg = ndecimal = 0; /* might have decimal in exponent */ne++;}elsegoto donehc;break;default:if (!isdigit(text[0]))goto donehc;break;}if (currentexp == 3) {if (isspace(c))goto donehc;if (clength == 0) {if (c != '.' && !isalpha(c))goto donehc;else if (c != '.' && !isalnum(c))goto donehc;}}if (clength++ > 29) {warning("spreadsheet: expression too long");clength--;goto donehc;}*bufp++ = text[0];printstring(buf, clength, crow, ccol);return;donehc: bell();}void printlabs(){char clab[10];int i;SEXP tppoint;if (length(inputlist) > colmin)tppoint = nthcdr(inputlist, colmin - 1);elsetppoint = R_NilValue;for (i = colmin; i <= colmax; i++)if (TAG(tppoint) != R_NilValue) {printstring(CHAR(PRINTNAME(TAG(tppoint))),strlen(CHAR(PRINTNAME(TAG(tppoint)))), 0, i - colmin + 1);tppoint = CDR(tppoint);}else {sprintf(clab, "var%d", i);printstring(clab, strlen(clab), 0, i - colmin + 1);}for (i = rowmin; i <= rowmax; i++) {sprintf(clab, "R %d", i);printstring(clab, strlen(clab), i - rowmin + 1, 0);}}#ifdef Macintosh/* Event Loop Routines */static int NextEvent(DEEvent * ioevent){long sleepTicks;sleepTicks = 10L;return (WaitNextEvent(everyEvent, ioevent, sleepTicks, 0L));}static int WhichEvent(DEEvent ioevent){int rval;rval = ioevent.what;return rval;}static void RefreshKeyboardMapping(DEEvent * ioevent){}static void doConfigure(DEEvent * ioevent){}static int IsModifierKey(KeySym key){return 0;}static KeySym GetKey(DEEvent * event){return (*event).message & keyCodeMask;}static char GetCharP(DEEvent * event){short iochar;iochar = (*event).message & charCodeMask;return (char) iochar;}static int CheckControl(DEEvent * event){return 0;}static void doControl(DEEvent * event){}/* Menu Functions */static void doSpreadMenu(long code){int theItem;theItem = LoWord(code);if (HiWord(code) == 200 && theItem == 1)doneSpread = 0;elsebell();}void popupmenu(int x_pos, int y_pos, int col, int row){DialogPtr dstorage;DEEvent theEvent;short whichItem, type;Rect box;ControlHandle chand1, chand2, chand3;char text[120], noString[1];SEXP tvec;int levs;noString[1] = '\0';if (length(inputlist) < col + colmin - 1)inputlist = listAppend(inputlist, allocList(col + colmin - 1 - length(inputlist)));tvec = nthcdr(inputlist, col + colmin - 2);if (TAG(tvec) != R_NilValue)sprintf(text, "%s", CHAR(PRINTNAME(TAG(tvec))));elsesprintf(text, "var%d", col + colmin - 1);dstorage = GetNewDialog(129, 0L, (WindowPtr) - 1L);GetDItem(dstorage, 2, &type, &chand1, &box);GetDItem(dstorage, 3, &type, &chand2, &box);if (TYPEOF(CAR(tvec)) == STRSXP) {SetCtlValue(chand1, 0);SetCtlValue(chand2, 1);}else {SetCtlValue(chand1, 1);SetCtlValue(chand2, 0);}GetDItem(dstorage, 5, &type, &chand3, &box);CtoPstr(text);SetIText(chand3, text);SelIText(dstorage, 5, 0, 120);ShowWindow(dstorage);whichItem = 0;while (whichItem != 6 && whichItem != 7) {ModalDialog(0, &whichItem);if (whichItem == 2) {SetCtlValue(chand1, 1);SetCtlValue(chand2, 0);}else if (whichItem == 3) {SetCtlValue(chand1, 0);SetCtlValue(chand2, 1);}else if (whichItem == 6) {GetDItem(dstorage, 5, &type, &chand3, &box);GetIText(chand3, text);PtoCstr(text);if (!validName(text)) {CtoPstr(text);ParamText(text, noString, noString, noString);StopAlert(130, 0L);SetIText(chand3,"");whichItem = 0;}else {TAG(tvec) = install(text);printstring(text, strlen(text), 0, col);type = GetCtlValue(chand1);if (type == 1) {if (CAR(tvec) == R_NilValue)CAR(tvec) = ssNewVector(REALSXP, 100);else {levs = LEVELS(CAR(tvec));CAR(tvec) = coerceVector(CAR(tvec), REALSXP);LEVELS(CAR(tvec)) = levs;}}else {if (CAR(tvec) == R_NilValue)CAR(tvec) = ssNewVector(STRSXP, 100);else {levs = LEVELS(CAR(tvec));CAR(tvec) = coerceVector(CAR(tvec), STRSXP);LEVELS(CAR(tvec)) = levs;}}}}}CloseDialog(dstorage);}static int validName(char *text){char tmp;if (!isalpha(*text++))return 0;while (*text != '\0')if (!isalnum(*text++))return 0;return 1;}/* X11-MAC versions of graphics functions */static void Mac_MoveTo(int x, int y){xlast = x;ylast = y;MoveTo(x, y);}/* ring the bell for argument 60ths of a second */static void bell(){SysBeep(30);}static int checkquit(int x){return 0;}static void clearwindow(){EraseRect(&dataentryWindow->portRect);}/*copyarea is a lot more complicated than you would expect but,the Mac requires that you transform to screen coordinates to CopyBitsin the same window and if the copy regions are not the same size itautomatically rescales for you (bizarre!) so you need to be sure thetwo regions are the same size*/static void copyarea(int src_x, int src_y, int dest_x, int dest_y){int destw, desth;Point tp1, tp2;Rect srcRect, destRect;destw = (src_x < dest_x) ? windowWidth - dest_x : windowWidth - src_x;desth = (src_y < dest_y) ? windowHeight - dest_y : windowHeight - src_y;tp1.h = src_x;tp1.v = src_y;LocalToGlobal(&tp1);tp2.h = src_x + destw;tp2.v = src_y + desth;LocalToGlobal(&tp2);SetRect(&srcRect, tp1.h, tp1.v, tp2.h, tp2.v);tp1.h = dest_x;tp1.v = dest_y;tp2.h = dest_x + destw;tp2.v = dest_y + desth;LocalToGlobal(&tp1);LocalToGlobal(&tp2);SetRect(&destRect, tp1.h, tp1.v, tp2.h, tp2.v);CopyBits(&screenBits, &screenBits,&srcRect, &destRect, srcCopy, 0);/*&srcRect, &destRect, srcCopy, &dataentryWindow->clipRgn); */}/* clear the text from an area */static void cleararea(int xpos, int ypos, int width, int height){SetRect(&drawRect1, xpos, ypos, xpos + width, ypos + height);EraseRect(&drawRect1);}static void drawline(int fromx, int fromy, int tox, int toy){if (xlast != fromx || ylast != fromy)Mac_MoveTo(fromx, fromy);LineTo(tox, toy);xlast = tox;ylast = toy;}static void drawrectangle(int xpos, int ypos, int width, int height){SetRect(&drawRect1, xpos, ypos, xpos + width + 1, ypos + height + 1);FrameRect(&drawRect1);}static void setattribsfromwindow(){windowWidth = dataentryWindow->portRect.right - dataentryWindow->portRect.left;windowHeight = dataentryWindow->portRect.bottom - dataentryWindow->portRect.top;box_w = textwidth(digits, 10);box_h = 25;bwidth = 0;}static void setforeground(int which){if (which == 0)PenPat(white);elsePenPat(black);}/* set the line width */static void setlineattribs(int width){PenSize(width, width);}/* Text Drawing */static void drawtext(int xpos, int ypos, char *text, int len){MoveTo(xpos + text_offset, ypos - text_offset);DrawText(text, 0, len);}static void querypointer(int *xglobal, int *yglobal, int *xlocal, int *ylocal){Point tp1;GetMouse(&tp1);*xlocal = tp1.h;*ylocal = tp1.v;LocalToGlobal(&tp1);*xglobal = tp1.h;*yglobal = tp1.v;}/* no need to Rsync on the mac */static void Rsync(){}/* find the width of a text string */static int textwidth(char *text, int nchar){int t1;t1 = TextWidth(text, 0, nchar);return t1;}/* Open/Close Windows */static int initwin(void){FontInfo info;dewindowBounds.left = screenBits.bounds.right - degraphBounds.right - 4;dewindowBounds.top = screenBits.bounds.bottom - degraphBounds.bottom - 4;dewindowBounds.right = screenBits.bounds.right - 4;dewindowBounds.bottom = screenBits.bounds.bottom - 4;dedragRect = screenBits.bounds;SetRect(&dedragRect,screenBits.bounds.left + 4,screenBits.bounds.top + 24,screenBits.bounds.right - 4,screenBits.bounds.bottom - 4);FlushEvents(everyEvent, 0);dataentryWindow = NewWindow(0L, &dewindowBounds, "\pR Data Entry", true, documentProc,(WindowPtr) - 1L, false, 0);spreadContext.active = 0; /* 1 only when locator in action */spreadContext.theWindow = dataentryWindow;spreadContext.doCursor = doSpreadCursor;spreadContext.doKey = doSpreadKey;spreadContext.doMenu = doSpreadMenu;spreadContext.doActive = doSpreadActive;spreadContext.doInactive = doSpreadInactive;spreadContext.doNotWindow = doSpreadNotWindow;spreadContext.doUpdate = doSpreadUpdate;spreadContext.doActivate = doSpreadActivate;GetPort(&savePort);SetPort(dataentryWindow);SelectWindow(dataentryWindow);TextSize(10);GetFontInfo(&info);fh = info.ascent + info.descent;fw = info.widMax + info.leading;deMenu = NewMenu(200, "\pDataEntry");AppendMenu(deMenu, "\pQuit");InsertMenu(deMenu, 0);DrawMenuBar();activeContext = &spreadContext;return 0;}static void closewin(){closerect();DeleteMenu(200);DisposeMenu(deMenu);DrawMenuBar();DisposeWindow(dataentryWindow);SetPort(savePort);activeContext = &stdioContext;}#else/* Event Loop Functions */static int NextEvent(DEEvent * ioevent){XNextEvent(iodisplay, ioevent);return 1;}static int WhichEvent(DEEvent ioevent){return ioevent.type;}static KeySym GetKey(DEEvent * event){int i;char text[1];KeySym iokey;i = XLookupString(event, text, 10, &iokey, 0);return iokey;}static char GetCharP(DEEvent * event){int i;char text[1];KeySym iokey;i = XLookupString(event, text, 1, &iokey, 0);return text[0];}static int CheckControl(DEEvent * event){return (*event).xkey.state & ControlMask;}static void doControl(DEEvent * event){int i;char text[1];KeySym iokey;(*event).xkey.state = 0;i = XLookupString(event, text, 1, &iokey, 0);if (text[0] == 'f')jumpwin(colmin, rowmax);else if (text[0] == 'b') {i = (1 > rowmin - nhigh) ? 1 : rowmin - nhigh;jumpwin(colmin, i);}}static void doConfigure(DEEvent * event){if ((windowWidth != (*event).xconfigure.width) ||(windowHeight != (*event).xconfigure.height))drawwindow();}static void RefreshKeyboardMapping(DEEvent * event){XRefreshKeyboardMapping(event);}/* Initialize/Close Windows */void closewin(){XFreeGC(iodisplay, iogc);XDestroyWindow(iodisplay, iowindow);XCloseDisplay(iodisplay);}/* set up the window, print the grid and column/row labels */int initwin(){int i, twidth;int ioscreen;unsigned long iowhite, ioblack;char ioname[] = "R DataEntryWindow";char digits[] = "123456789.0";Window root;XEvent ioevent;XSetWindowAttributes winattr;if ((iodisplay = XOpenDisplay(NULL)) == NULL)return (1);/* Get Font Loaded if we can */font_info = XLoadQueryFont(iodisplay, font_name);if (font_info == NULL)return 1; /* ERROR *//* find out how wide the input boxes should be and set up the window size defaults */twidth = textwidth(digits, strlen(digits));box_w = twidth + 4;box_h = font_info->max_bounds.ascent + font_info->max_bounds.descent + 4;text_offset = 2 + font_info->max_bounds.descent;windowWidth = 6 * box_w;windowHeight = 26 * box_h + hwidth;ioscreen = DefaultScreen(iodisplay);iowhite = WhitePixel(iodisplay, ioscreen);ioblack = BlackPixel(iodisplay, ioscreen);hand_cursor = XCreateFontCursor(iodisplay, XC_hand2);iohint.x = 0;iohint.y = 0;iohint.width = windowWidth;iohint.height = windowHeight;iohint.flags = PPosition | PSize;root = DefaultRootWindow(iodisplay);if ((iowindow = XCreateSimpleWindow(iodisplay,root,iohint.x,iohint.y,iohint.width,iohint.height,bwidth,ioblack,iowhite)) == 0)return 1;XSetStandardProperties(iodisplay, iowindow, ioname, ioname, None,ioname, 0, &iohint);winattr.backing_store = Always;XChangeWindowAttributes(iodisplay, iowindow, CWBackingStore, &winattr);iogc = XCreateGC(iodisplay, iowindow, 0, 0);XSetFont(iodisplay, iogc, font_info->fid);XSetBackground(iodisplay, iogc, iowhite);setforeground(1);XSelectInput(iodisplay, iowindow,ButtonPressMask | KeyPressMask | ExposureMask | StructureNotifyMask);XMapRaised(iodisplay, iowindow);/* now set up the menu-window, for now use the same text dimensions as above */menuwindow = XCreateSimpleWindow(iodisplay, root, 0, 0, twidth,4 * box_h, 2, ioblack, iowhite);for (i = 0; i < 4; i++) {menupanes[i] = XCreateSimpleWindow(iodisplay, menuwindow, 0, box_h * i,twidth, box_h, 1, ioblack, iowhite);XSelectInput(iodisplay, menupanes[i],ButtonPressMask | ButtonReleaseMask | ExposureMask);}/* XMapSubwindows(iodisplay, menuwindow); */winattr.override_redirect = True;XChangeWindowAttributes(iodisplay, menuwindow, CWBackingStore | CWOverrideRedirect, &winattr);Rsync();/* this next sequence makes sure the window is up and ready before you start drawing in it */XNextEvent(iodisplay, &ioevent);if (ioevent.xany.type == Expose) {while (ioevent.xexpose.count)XNextEvent(iodisplay, &ioevent);}drawwindow();return 0;}/* MAC/X11 BASICS */static void bell(){XBell(iodisplay, 20);}static void cleararea(int xpos, int ypos, int width, int height){XClearArea(iodisplay, iowindow, xpos, ypos, width, height, 0);}#ifdef OLDstatic void clearwindow(){XClearWindow(iodisplay, iowindow);}#endifstatic void copyarea(int src_x, int src_y, int dest_x, int dest_y){XCopyArea(iodisplay, iowindow, iowindow, iogc, src_x, src_y,windowWidth - src_x, windowHeight - src_y, dest_x, dest_y);Rsync();}static void drawline(int fromx, int fromy, int tox, int toy){XDrawLine(iodisplay, iowindow, iogc, fromx, fromy, tox, toy);}static void drawrectangle(int xpos, int ypos, int width, int height){XDrawRectangle(iodisplay, iowindow, iogc, xpos, ypos, width, height);}static void drawtext(int xpos, int ypos, char *text, int len){XDrawImageString(iodisplay, iowindow, iogc, xpos,ypos, text, len);Rsync();}static void querypointer(int *xr, int *yr, int *xw, int *yw){unsigned int keys;Window root, child;XQueryPointer(iodisplay, iowindow, &root, &child, xr, yr, xw, yw, &keys);}static void setattribsfromwindow(){XWindowAttributes attribs;XGetWindowAttributes(iodisplay, iowindow, &attribs);windowWidth = attribs.width;windowHeight = attribs.height;bwidth = attribs.border_width;}static void setforeground(int which){if (which == 0)XSetForeground(iodisplay, iogc, WhitePixel(iodisplay,DefaultScreen(iodisplay)));elseXSetForeground(iodisplay, iogc, BlackPixel(iodisplay,DefaultScreen(iodisplay)));}static void setlineattribs(int width){XSetLineAttributes(iodisplay, iogc, width, LineSolid, CapRound, JoinRound);}static void Rsync(){XSync(iodisplay, 0);}static int textwidth(char *text, int nchar){int t1;t1 = XTextWidth(font_info, text, nchar);return t1;}/* Menus */void popupmenu(int x_pos, int y_pos, int col, int row){int i, button, levs;char name[20];XEvent event;Window selected_pane;SEXP tvec;XMapSubwindows(iodisplay, menuwindow);XMapRaised(iodisplay, menuwindow);XMoveWindow(iodisplay, menuwindow, x_pos, y_pos);/* now fill in the menu panes with the correct information */if (length(inputlist) < col + colmin - 1)inputlist = listAppend(inputlist, allocList(col + colmin - 1 - length(inputlist)));tvec = nthcdr(inputlist, col + colmin - 2);if (TAG(tvec) != R_NilValue)sprintf(name, " %s", CHAR(PRINTNAME(TAG(tvec))));elsesprintf(name, " COLUMN %d", col + colmin - 1);XDrawString(iodisplay, menupanes[0], iogc, 3, box_h - 3, name, strlen(name));for (i = 1; i < 4; i++)XDrawString(iodisplay, menupanes[i], iogc, 3, box_h - 3,menu_label[i - 1], strlen(menu_label[i - 1]));if (CAR(tvec) == R_NilValue || TYPEOF(CAR(tvec)) == REALSXP)XDrawString(iodisplay, menupanes[1], iogc, box_w - 20, box_h - 3,"X", 1);elseXDrawString(iodisplay, menupanes[2], iogc, box_w - 20, box_h - 3,"X", 1);/*start an event loop; we're looking for a button press and a buttonrelease in the same window*/while (1) {XNextEvent(iodisplay, &event);if (event.type == ButtonPress) {button = event.xbutton.button;selected_pane = event.xbutton.window;while (1) {while (XCheckTypedEvent(iodisplay, ButtonPress, &event));XMaskEvent(iodisplay, ButtonReleaseMask, &event);if (event.xbutton.button == button)break;}if (selected_pane == event.xbutton.window) {for (i = 0; selected_pane != menupanes[i]; i++);switch (i) {case 0:bell();break;case 1:if (CAR(tvec) == R_NilValue)CAR(tvec) = ssNewVector(REALSXP, 100);levs = LEVELS(CAR(tvec));CAR(tvec) = coerceVector(CAR(tvec), REALSXP);LEVELS(CAR(tvec)) = levs;goto done;case 2:if (CAR(tvec) == R_NilValue)CAR(tvec) = ssNewVector(STRSXP, 100);levs = LEVELS(CAR(tvec));CAR(tvec) = coerceVector(CAR(tvec), STRSXP);LEVELS(CAR(tvec)) = levs;goto done;case 3:closerect();ccol = col;crow = 0;clearrect();goto done;}}} /* this doesn't work and perhaps I should move it up to the main control loop */else if (event.type == Expose) {if (event.xexpose.window == menuwindow) {XDrawString(iodisplay, menupanes[0], iogc, 3, box_h - 3,name, strlen(name));for (i = 1; i < 4; i++)XDrawString(iodisplay, menupanes[i], iogc, 3, box_h - 3,menu_label[i - 1], strlen(menu_label[i - 1]));}}}done: popdownmenu();highlightrect();}void popdownmenu(){XUnmapWindow(iodisplay, menuwindow);XUnmapSubwindows(iodisplay, menuwindow);}#endif#elseSEXP do_dataentry(SEXP call, SEXP op, SEXP args, SEXP rho){error("no data entry editor in this version of R");return R_NilValue;}#endif