.packageName <- "iSPlot"
##Copyright R. Gentleman, 2003, all rights reserved.

setOldClass("GtkWindow")
setOldClass("GtkDrawingArea")
setOldClass("GtkCList")

##compute the dimensions of a GTK drawing area in pixels

MMPI = 25.4

devPixel <- function()
{
    c(width=devWidthPixels(), height=devHeightPixels())
}

devHeightPixels <- function() 
{
    hI <- par("din")[2]
    hI/(gdkScreenHeightMm()/gdkScreenHeight()/MMPI)
}

devWidthPixels <- function() 
{
    wI <- par("din")[1]
    wI/(gdkScreenWidthMm()/gdkScreenWidth()/MMPI)
}

##some helpful constants, PixelsPerInchWide/High;
##these are fixed for a display
getPixPerInch<-function()
{
  PPIw <- 1/(gdkScreenWidthMm()/gdkScreenWidth()/MMPI)
  PPIh <- 1/(gdkScreenHeightMm()/gdkScreenHeight()/MMPI)
  return(list(PPIw=PPIw,PPIh=PPIh))
}

##translate from pixels, measured form (0,0) in upper left
##to inches measured from (0,0) in bottom left
pix2inches <- function(x,y) 
{
    PPI<-getPixPerInch()
    PPIw<-PPI$PPIw
    PPIh<-PPI$PPIh

    xi <- x/PPIw
    yi <- par("din")[2] - y/PPIh
    if( any(xi < 0) | any(yi < 0) )
        stop("mapped outside the device")
    return(list(x=xi,y=yi))
}

## from inches we map to usr coords
## we use, pin, usr and mai to get all needed pieces
inches2usr <- function(x, y) 
{
  # need to use the correct user coordinates for the plotting region
  # note: can have multiple plots
  #print(getActivePlot()$plotDevice)
  usr<-plotPar(getActivePlot())$usr  
    pin <- par("pin") ##width, height
    #usr <- par("usr")
    mai <- par("mai")
    #print(paste("pin",pin))
    #print(paste("usr",usr))
    #print(paste("mai",mai))
    xu <- usr[1] + (x-mai[2])*(usr[2]-usr[1])/pin[1]
    yu <- usr[3] + (y-mai[1])*(usr[4]-usr[3])/pin[2]
    return(list(x=xu,y=yu))
}







# will need to load RGtk and gtkDevice

# two environments for the model and the views
dataEnv<-new.env()
viewEnv<-new.env()

assign("defaultPlotData", list(color="black", pch=1, highlit=FALSE, 
        hide=FALSE), dataEnv)
assign("dfList", list(), dataEnv)
# highColor is the color that shows up when highlit is TRUE
assign("highColor", "red", dataEnv)

assign("viewList", list(), viewEnv)
# make a variable to store the active view
assign("activeView", c(), viewEnv)
# need to store the identify points
assign("curIDpoint", list(), viewEnv)
# the color of the text of the main title on the plots
assign("mainTitleColor", "red", viewEnv)


#################
# loadDFData is a function to correctly load a dataframe:
#  - the dataframe is added to dataEnv (added to dfList)
#  - columns are added for defaultPlotData
# all dataframes must be added to the data environment through this function
# dataF is the dataframe and dfName is the name of the dataframe (a string)
#
# may want to check if the defaultPlotData has already been added
#################
loadDFData<-function(dataF,dfName)
{
  # need to add columns to the dataframe that pertain to its view
  numRow<-dim(dataF)[1]
  
  # convert matrices to dataframes
  if (is.matrix(dataF))
    dataF<-as.data.frame(dataF)

  # first need to check that the columns have not already been
  # added to the data
  if (!all(names(defaultPlotData) %in% names(dataF)))
    # want to add color, pch, highlit, and hide for now
    # defaults: color="black" ,pch=1, highlit=FALSE, hide=FALSE
    for (i in 1:length(defaultPlotData))
    {
      curDefault<-rep(defaultPlotData[[i]],numRow)
      curName<-names(defaultPlotData)[i]
      dataF<-addColumn(dataF,curDefault,curName)
    }

  # need to add this dataframe to dfList
  namesDFL<-names(dfList)
  if (is.null(namesDFL))
    dfList[[dfName]]<-list(curDF=dataF,origDF=dataF)
  else
  {
    # check to make sure the name is not in use
    if (!(dfName %in% namesDFL))
      dfList[[dfName]]<-list(curDF=dataF,origDF=dataF)
    else
    {
      # or should we just update this list element????
      #stop("Name already in use")
    }
  }

  assign("dfList",dfList,dataEnv)
}
environment(loadDFData)<-dataEnv

#########
# check if added data has views that depend on it
# if so then need to update views
#########
checkIfPlotted<-function(dataName)
{
  viewList<-get("viewList", viewEnv)
  if (length(viewList) > 0)
  {
    # any views that depend on this data
    namesInUse<-unlist(lapply(viewList, function(x) {dataName(x)}))
    if (dataName %in% namesInUse)
    {
      # now need to replot
      updateViews(dataName,type="reset")
    }
  }
}

#################
# addColumn is a function to add a column to a dataframe
# only tests that the data vector is the correct length
# dataF is the dataframe, data is the vector, colName is the column name (a string)
#################
addColumn<-function(dataF,data,colName)
{
  # do we need to check lengths?
  numRow<-dim(dataF)[1]
  numCol<-dim(dataF)[2]

  if (length(data)==numRow)
  {
    dataF[,numCol+1]<-data
    # add the column name
    names(dataF)[numCol+1]<-colName    
  }
  else
    stop("Incorrect length")
  return(dataF)
}

#################
# checkIndivTypes is a function to check whether the data
# element has the same data type as the data vector
#################
checkIndivTypes<-function(datVec,datEle)
{
  typesMatch<-TRUE
  
  if (is.numeric(datVec) && !is.numeric(datEle))
    typesMatch<-FALSE
  if (is.character(datVec) && !is.character(datEle))
    typesMatch<-FALSE
  if (is.logical(datVec) && !is.logical(datEle))
    typesMatch<-FALSE
  if (is.factor(datVec))
  {
    # need to check if datEle is one of the factors
    if (!(datEle %in% levels(datVec)))
      typesMatch<-FALSE
  }

  return(typesMatch)
}

#################
# modify is a function to change one or more attributes of one or more rows in
# a dataframe
# dfName is the dataframe name, 
# Rname is the row name or index, 
# Cname is the column name or index,
# data is the new data to use (will be a list)
#################
modify<-function(dfName,Rname,Cname,data)
{
  dataF<-getData(type="dataframe",name=dfName)
 
  if (is.null(dataF))
    stop("Incorrect data frame name")

  # name must be a rowIndex,colIndex vector or it must be a rowName,
  # colName vector
  rowIndex<-Rname
  colIndex<-Cname

  rowNames<-row.names(dataF)
  colNames<-names(dataF)
 
  # use all() in case modifying more than one point at a time
  if ( !(all(rowIndex %in% rowNames) || (all(is.numeric(rowIndex)) && 
        (all(rowIndex>=1) && all(rowIndex<=dim(dataF)[1])))) )
    stop("Incorrect Row to Modify")
  if ( !(all(colIndex %in% colNames) || (all(is.numeric(colIndex)) && 
        (all(colIndex>=1) && all(colIndex<=dim(dataF)[2])))) )  
    stop("Incorrect Column to Modify")

  # then should ensure that the data is of correct type
  # need to compare the data point to the column values
  if (!all(is.numeric(colIndex)))
    colIndex<-match(colIndex,colNames)
  if (!all(is.numeric(rowIndex)))
    rowIndex<-match(rowIndex,rowNames)
  
  if (length(data) != length(colIndex) || length(colIndex) != 
                                          length(rowIndex))
  {
    stop("Must have equal row length, column length and data length")
  }
  else
    for (i in 1:length(colIndex))
    {
      typesMatch<-checkIndivTypes(dataF[,colIndex[i]],data[[i]])
      if (typesMatch)
        dataF[rowIndex[i],colIndex[i]]<-data[[i]]
    }

  updateData(type="dataframe",name=dfName,newData=dataF)
}
environment(modify)<-dataEnv

#################
# reset is a function to reset the dataframe to its original state
# dfName is the dataframe name
#################
reset<-function(dfName)
{
  updateData(type="dataframe", name=dfName, 
             newData=dfList[[dfName]][["origDF"]])
}
environment(reset)<-dataEnv

######
# updates all views (replaces updateSpreadsheet and updatePlots),
# including spreadsheets and plots
######
updateViews<-function(dfName,type,rowName=c())
{
  # create a gUpdateViewMessage
  plMessage<-new("gUpdateViewMessage",type,Rname=rowName)

  # then we need to update these plots
  handleMessage(plMessage,dataName=dfName)
}

##########
# a function to update points on a plot (no replotting)
#########
updatePoints<-function(plotIndex,Rname)
{
  # need to get the information for points function
  # this will include the rowname that has changed
  rowName<-Rname

  # if the same rowName is repeated, then just use the first one
  if (all(rowName==rowName[1]))
    rowName<-rowName[1]

  curPlot<-viewList[[plotIndex]]

  # need to convert rowName to a rowIndex
  dfName<-dataName(curPlot)
  dfList<-get("dfList",dataEnv)
  dataF<-getData(type="dataframe",name=dfName)

  curpar<-plotPar(curPlot)

  # name must be a rowIndex,colIndex vector or it must be a 
  # rowName,colName vector
  rowIndex<-Rname

  rowNames<-row.names(dataF)
  colNames<-names(dataF) 

  # use all() if there is more than one row changed
  if ( !(all(rowIndex %in% rowNames) || (all(is.numeric(rowIndex)) && 
        (all(rowIndex>=1) && all(rowIndex<=dim(dataF)[1])))) )
    stop("Incorrect Row to Modify")

  if (!all(is.numeric(rowIndex)))
    rowIndex<-match(rowIndex,rowNames)

  pchIndex<-match("pch",colNames)
  colIndex<-match("color",colNames)
  highIndex<-match("highlit",colNames)
  hideIndex<-match("hide",colNames)

  # need to set the active device
  curDev<-plotDevice(curPlot) 
  dev.set(curDev)

  # set the usr parameter
  par("usr"=curpar$usr)

  colx<-colx(curPlot)
  coly<-coly(curPlot)

  xval<-dataF[rowIndex,colx]
  yval<-dataF[rowIndex,coly]
  colval<-dataF[rowIndex,colIndex]
  pchval<-dataF[rowIndex,pchIndex]
  highval<-dataF[rowIndex,highIndex]
  hideval<-dataF[rowIndex,hideIndex]

  bgval<-curpar$bg
  cexval<-curpar$cex
  if (bgval=="transparent")
    bgval<-"white"
  
  # make sure to remove any highlighting if highval==FALSE
  if (any(highval==FALSE))
  {
    Findeces<-which(highval==FALSE)

    # use a pch of 1 for highlighting regardless of what the pch value for 
    # the row is (don't want the highlighted point to be filled in)
    # pch=1 added 5/4/04
    points(xval[Findeces], yval[Findeces], cex=cexval+1, col=bgval, pch=1)
  }

  points(xval,yval,col=colval,pch=pchval)

  # also need to make points for highlit
  if (any(highval==TRUE))
  {
    Tindeces<-which(highval==TRUE)

    # use a pch of 1 for highlighting regardless of what the pch value for 
    # the row is (don't want the highlighted point to be filled in)
    # pch=1 added 5/4/04
    points(xval[Tindeces], yval[Tindeces], cex=cexval+1, 
           col=get("highColor",dataEnv), pch=1)
  }

  # need to hide points
  if (any(hideval))
  {
    Tindeces<-which(hideval==TRUE)
    # make sure to fill in the point as the background in case it is colored
    points(xval[Tindeces], yval[Tindeces], col=bgval, pch=19)
  }

  hideAndHigh<-highval & hideval
  if (any(hideAndHigh))
  {
    Tindeces<-which(hideAndHigh==TRUE)
    points(xval[Tindeces], yval[Tindeces], cex=cexval+1, col=bgval)
  }
}
environment(updatePoints)<-viewEnv

#################
# setParamList is a function to set the parameter list for making a 
# scatterplot
# dataF is the dataframe and dfRows are the dataframe rows
#################
setParamList<-function(dataF,dfRows,plotObject)
{
  colIndex<-match("color",names(dataF))
  curColors<-dataF[dfRows,colIndex]
  pchIndex<-match("pch",names(dataF))
  curPch<-dataF[dfRows,pchIndex]
  hideIndex<-match("hide",names(dataF))
  curHide<-dataF[dfRows,hideIndex]
  highIndex<-match("highlit",names(dataF))
  curHigh<-dataF[dfRows,highIndex]
  
  # make the points that are hidden be the background color
  plotBG<-plotPar(plotObject)$bg
  curColors[curHide]<-plotBG

  return(list(col=curColors,pch=curPch))
}
environment(setParamList)<-viewEnv

########
# get the data field for gUpdateDataMessage object using the change type 
# and view mode
########
getChangeDFData<-function(curPlot,curPoint)
{
  plotDfName<-dataName(curPlot)
  dataF<-getData(type="dataframe",name=plotDfName)
 
  viewMode<-get("viewMode",controlEnv)
  numRowsToChange<-length(curPoint$closestPoint)

  if (viewMode=="highlight" || viewMode == "brush" || 
        viewMode == "identify")
  {
    colName<-rep("highlit",numRowsToChange)
    # rowName will already have the same length as colName, because
    # the length of curPoint$closestPoint is numRowsToChange
    rowName<-curPoint$closestPoint
      
    # want to toggle highlight value for now
    booHigh<-!dataF[match(curPoint$closestPoint, row.names(dataF)), 
                      match("highlit",names(dataF))] 
  
    datalist<-vector(mode="list", length=numRowsToChange)
    datalist[1:numRowsToChange]<-booHigh

    cData<-list(dfName=plotDfName, Rname=rowName, Cname=colName, 
                  data=datalist)
  } 
  # then we are either changing color or pch or both
  if (viewMode=="color")
  {
    colName<-c(rep("color", numRowsToChange), rep("pch",numRowsToChange))
    rowName<-rep(curPoint$closestPoint, 2)
      
    datalist<-vector(mode="list", length=(2*numRowsToChange))
    datalist[1:numRowsToChange]<-rep(get("currentColor", controlEnv),
                                       numRowsToChange)
    datalist[(numRowsToChange+1):(2*numRowsToChange)]<-rep(get("fillPch",
                                       controlEnv), numRowsToChange)

    cData<-list(dfName=plotDfName, Rname=rowName, Cname=colName, 
                  data=datalist)
    # will need to store new pch/color in environment variable in controlEnv
  }
  if (viewMode=="hide")
  {
    colName<-rep("hide",numRowsToChange)
    # rowName will already have the same length as colName, because
    # the length of curPoint$closestPoint is numRowsToChange
    rowName<-curPoint$closestPoint

    # want to toggle hide value for now
    booHide<-!dataF[match(curPoint$closestPoint, row.names(dataF)), 
                      match("hide",names(dataF))] 
   
    datalist<-vector(mode="list", length=numRowsToChange)
    datalist[1:numRowsToChange]<-booHide

    cData<-list(dfName=plotDfName, Rname=rowName, Cname=colName, 
                  data=datalist)    
  }

  return(cData)
}

##################
# identifyPoint determines which point on the scatterplot
# the user clicked (or NULL if no point was clicked)
#
# curplot is the plot information (one element from viewList)
# xyloc is the x and y location that was clicked
##################
identifyPoint<-function(curplot,xyloc)
{
#  print(xyloc)
  # get the cxy of the active plot
  cxy<-par("cxy")
  pointWidth<-cxy[1]
  pointHeight<-cxy[2]  

  plotDf<-dataName(curplot)
  plotRows<-dfRows(curplot)
  plotX<-colx(curplot)
  plotY<-coly(curplot)

  dfList<-get("dfList",dataEnv)
  curData<-getData(type="dataframe",name=plotDf)
  x<-curData[plotRows,plotX]
  y<-curData[plotRows,plotY]

  # check if x or y is a factor
  if (is(x,"factor"))
    x<-as.numeric(x)
  if (is(y,"factor"))
    y<-as.numeric(y)

  rowNames<-row.names(curData)

  # now need to identify the closest point
  totdiff<-(xyloc$x-x)^2+(xyloc$y-y)^2
  booClosest<-min(totdiff)==totdiff
  
  closestPoint<-rowNames[booClosest]
  closestXY<-list(x=x[booClosest],y=y[booClosest])
#  print(closestXY)

  # need to ensure that the closestPoint was actually clicked
  # use cxy
  x.within<-FALSE
  y.within<-FALSE

  if ((xyloc$x<=closestXY$x) && (xyloc$x>=closestXY$x-(pointWidth/2)))
      x.within<-TRUE
  if ((xyloc$x>closestXY$x) && (xyloc$x<=closestXY$x+(pointWidth/2)))
      x.within<-TRUE  

  if ((xyloc$y<=closestXY$y) && (xyloc$y>=closestXY$y-(pointHeight/2)))
      y.within<-TRUE
  if ((xyloc$y>closestXY$y) && (xyloc$y<=closestXY$y+(pointHeight/2)))
      y.within<-TRUE 

  if (x.within && y.within)
    return(list(closestXY=closestXY,closestPoint=closestPoint))
  else
    return(NULL)
}

#############
# remove the devices from R's device list
#############
removeDevices<-function(curDev)
{
  # remove the devices from R's device list
  sapply(curDev,function(x){dev.off(x)},USE.NAMES=FALSE)
}

################
# getPlotNumber gives the index in viewList of the current device
################
getPlotNumber<-function(curDev)
{
  allPlots<-getPlotsFromViews()
  dev.ele<-getPlotDev(allPlots)
  plotInDev<-match(curDev,dev.ele)
  return(plotInDev)
}

################
# getPlotDev makes a vector of the device element in the viewList
# must make sure that viewList contains only elements of class plotView
################
getPlotDev<-function(viewList)
{
  dev.ele<-lapply(viewList, function(x){plotDevice(x)})
  dev.ele<-unlist(dev.ele)
  return(dev.ele)
}

################
# getPlotByDev gets the active plot by the device number
# curDev is the device
################
getPlotByDev<-function(curDev)
{
  plotInDev<-getPlotNumber(curDev)
  allPlots<-getPlotsFromViews()
 
  if (!is.na(plotInDev))
    return(allPlots[[plotInDev]])
  else
  {
    stop("Device does not exist")
  }
}

################
# getActivePlot determines which plot is active based
# on which device is active
################
getActivePlot<-function()
{
  activeDev<-as.numeric(dev.cur())
  activePlot<-getPlotByDev(activeDev)
  return(activePlot)
}

################
# addDrawingArea adds a drawing area to a gtk widget
#
# wid is the widget, dr is the drawing area,
# width is the drawing area width, height is the drawing area height,
# devVec is the device vector for the gtk window that will contain this
# drawing area,
# addType can be either 1,2,3,4 or 5 depending on which widget the drawing
# area is being added to (1=window,2=2nd pane,3=notebook,4=scrolled window,5=hbox or vbox)
# lab is the label, devName is the device name
################
addDrawingArea<-function(wid, dr, width, height, devVec, addType=1,
                         lab="", devName="")
{
  # set up the drawing area
  asGtkDevice(dr)
  dr$SetUsize(width,height)

  # 1 is for normal addition to a window, 
  # 2 is for addition to vertical or horizontal pane (2nd pane),
  # 3 is for addition to a notebook, 4 is for addition to a scrolled window
  # 5 for adding to box (horizontal or vertical)
  switch(addType,
         wid$Add(dr),
         wid$Add2(dr),
         wid$AppendPage(dr,gtkLabel(lab)),
         wid$AddWithViewport(dr),
         gtkBoxPackStart(wid,dr))

  # add to the device vector for the widget
  devVec<-dev.cur()
  
  # return the updated device vector
  return(devVec)
}


################
# removeEnvView removes views from viewList based on the
# device number or the data name depending on whether
# the class is plotView or spreadView
# viewItem is the view object to remove from viewList
################
removeEnvView<-function(viewItem)
{
  isPlot<-is(viewItem,"plotView")
  isSpread<-is(viewItem,"spreadView")  

  allPlotIndex<-unlist(lapply(viewList, function(x) {is(x,"plotView")}))
  allPlotItems<-viewList[allPlotIndex]
  allSpreadIndex<-unlist(lapply(viewList, function(x) {is(x,"spreadView")}))

  # find the indices of the plotItems
  remIndex<-c()
  if (isPlot)
  {
    allPlotIndex<-which(allPlotIndex)
    # the devices to remove
    deviceToRemove<-plotDevice(viewItem)
    # all the device numbers
    dev.num<-getPlotDev(allPlotItems)
    plotIndicesToRemove<-dev.num %in% deviceToRemove
    remPlotIndex<-allPlotIndex[plotIndicesToRemove] 
    remIndex<-remPlotIndex
  }
  # now find the indices of the spreadItems
  if (isSpread)
  {
    spreadName<-dataName(viewItem)
    allViewNames<-unlist(lapply(viewList, function(x) {dataName(x)}))
    remSpreadIndex<-which(allSpreadIndex & (allViewNames %in% spreadName))
    remIndex<-remSpreadIndex
  } 

  if (length(viewList)>0 && length(remIndex)>0)
    viewList[remIndex]<-c()
  
  # update the view list
  assign("viewList",viewList,viewEnv)  

  if (length(get("viewList",viewEnv)) == 0)
  {
    # then activeView must be empty since there are no views
    assign("activeView",c(),viewEnv) 
  }
}
environment(removeEnvView)<-viewEnv

################
# setDeleteEvents removes views from the viewList
# viewItem is an object of type view
################
setDeleteEvents<-function(viewItem)
{
  # remove from R's device list
  if (is(viewItem, "plotView"))
    removeDevices(plotDevice(viewItem))
      
  # need to also remove views from viewList
  removeEnvView(viewItem)

  # need to remove the menu item from the menu
  gtkMenuItemRemoveSubmenu(get("windowItem",controlEnv))
  # need to remove the accelerators from keyVal in controlEnv
  removeAccelerators()
  # now add back the window menu to the control window
  addWindowMenu()

  win(viewItem)$Destroy()     
}

#######
# will remove all accelerators that have an element winNum
#######
removeAccelerators<-function()
{
  keyVals<-get("keyVals",controlEnv)
  for (i in length(keyVals):1)
  {
    if ("winNum" %in% names(keyVals[[i]]))
    {
      keyVals[[i]]<-c()
    }
  }
  assign("keyVals",keyVals,controlEnv)
}

################
# closeWin adds a callback to the window for the delete
# event, which closes all devices open in the window
################
closeWin<-function(viewItem)
{
  win<-win(viewItem)

  win$AddCallback("delete_event",
    function(x,y)
    {    
      # x is the widget and y is the event type

      setDeleteEvents(viewItem)            
    }
  )
}

##########
# createKeyPressEvent is to add pseudo-accelerators to any 
# gtk window
##########
createKeyPressEvent<-function(w)
{
  # add a key press event to the window so it can seem like the user can use
  # the accelerators from the control window on any active gtk window
  gtkAddCallback(w,"key-press-event",
    function(obj,ev)
    {
      curStr<-gdkEventKeyGetString(ev)
      curState<-gdkEventKeyGetState(ev)
      curKeyval<-gdkEventKeyGetKeyval(ev)

      setKeyValToAction(curStr,curState,curKeyval)
    }
  )
}

##############
# setKeyValToAction uses the controlEnv variable, keyVals, to match
# a key press to the appropriate action
##############
setKeyValToAction<-function(curStr,curState,curKeyval)
{
  keyVals<-get("keyVals",controlEnv)

  assignedKeyVals<-unlist(lapply(keyVals,function(x){x$keyval}))
  assignedCurActions<-unlist(lapply(keyVals,function(x){x$action}))
  
  # what if the case is upper vs. lower?
  # note: having the Caps lock down changes the state
  # curState=4 when Ctrl has been pressed
  # curState=6 when Caps Lock is on and Ctrl has been pressed
  if (curState == 4 || curState == 6)
  {
    index<-which(assignedKeyVals %in% curKeyval)
    
    # first test if we just have the wrong capitalization
    if (length(index)==0)
    {
      # 32 is the difference between lower and upper caps on the same letter
      index<-which(assignedKeyVals %in% (curKeyval+32))
    }

    if (length(index) > 0)
    {
      if (length(index) > 1)
      {
        # need to find the correct index; the one where curAction != ""
        index<-index[assignedCurActions[index] != ""]
      }

      curAction<-assignedCurActions[index]

      # check if there are any extra parameters
      # each keyVals item should have 3 elements: keyval, action, and
      # accelChar so anything after the 3 is an extra parameter 
      if (length(keyVals[[index]]) > 3)
      {
        totalLen<-length(keyVals[[index]])
        updateCurAction(curAction, unlist(keyVals[[index]][4:totalLen]))
      }
      else
      {
        updateCurAction(curAction)
      }
    }
  }
}

###########
# checkPoint sees if the x,y values fall in a point from the dataframe
# only called when the viewMode is identify
###########
checkPoint<-function(curx,cury,curPlot)
{
  # expecting this to be a plot
  activeView<-get("activeView",viewEnv) 
#  print(activeView) 

  # set the user parameter
  curPar<-plotPar(curPlot)
  par("usr"=curPar$usr)

  curbg<-plotPar(curPlot)$bg
  if (curbg=="transparent")
    curbg<-"white"

  if (is(activeView,"plotView"))
  {
    xyloc<-list(x=curx,y=cury)

    # convert to user coordinates
    xyInches<-pix2inches(curx,cury)
    xyUsr<-inches2usr(xyInches$x,xyInches$y)
 
    curpt<-identifyPoint(curPlot,xyUsr)

    if (!is.null(curpt))
    {
      # need to keep information about the current and old points
      curPoint<-get("curIDpoint",viewEnv)

      continue<-FALSE
      if (length(curPoint$closestPoint) != length(curpt$closestPoint))
        continue<-TRUE
      else
        if (any(curPoint$closestPoint != curpt$closestPoint))
          continue<-TRUE
      if (length(curPoint)==0)
        continue<-TRUE

      if (continue)
      {
        # remove old point highlight before adding new text
        if (length(curPoint) > 0)
        {
          dfMessage<-new("gUpdateDataMessage", from=curPlot, where=curPoint)
          handleMessage(dfMessage)

          printText(curPoint,curPlot,curbg)
        }
        # now update new point
        assign("curIDpoint",curpt,viewEnv)    

        dfMessage<-new("gUpdateDataMessage", from=curPlot, where=curpt)

        # now need to handle the message
        handleMessage(dfMessage)      

        printText(curpt,curPlot,get("highColor",dataEnv))
      }
    }

    else
    {
      curPoint<-get("curIDpoint",viewEnv)
      # also need to remove old point highlight
      if (length(curPoint) > 0)
      {
        dfMessage<-new("gUpdateDataMessage", from=curPlot, where=curPoint)
        handleMessage(dfMessage)

        assign("curIDpoint",list(),viewEnv)
        printText(curPoint,curPlot,curbg)
      }
    }
  }
}

##########
# printText prints the row name next to a point
##########
printText<-function(curPoint,curPlot,color)
{
  closestXY<-curPoint$closestXY
  closestPoint<-curPoint$closestPoint

  curpar<-plotPar(curPlot)
  curusr<-curpar$usr
  par("usr"=curusr)
  # get x midpoint
  xMidPoint<-(curusr[1]+curusr[2])/2
  xCol<-colx(curPlot)
  # if the point is on the left side of the plot, print the row name
  # to the right, and vice versa
  # 4 = print to right, 2 = print to left
  if (length(closestXY$x) > 1)
  {
    if (closestXY$x[1] < xMidPoint)
      pos<-4
    else
      pos<-2
  }
  else
  {  
    if (closestXY$x < xMidPoint)
      pos<-4
    else
      pos<-2
  }
  text(closestXY$x,closestXY$y,closestPoint,pos=pos,col=color)
}

###########
# called whenever a view is being made
###########
createView<-function(type,dataName,...)
{
  booCreate<-TRUE
  # check if the dataframe is already being shown in a spreadsheet
  if (type=="spreadView")
  {
    viewList<-get("viewList",viewEnv)
    booSpreadView<-unlist(lapply(viewList, function(x) {is(x,"spreadView")}))
    # make sure that there are some spread views
    if (any(booSpreadView))
    {
      curNames<-unlist(lapply(viewList, function(x) {dataName(x)}))
      spreadNames<-curNames[booSpreadView]
      # only create new view if name is not in spread view names
      booCreate<-!(dataName %in% spreadNames)
    }
  }

  if (booCreate)
  {
    # create the window
    win<-gtkWindow(show=FALSE)     
         
    if (type=="spreadView")
    {
      newView<-createDataView(win,dataName)
    }
    if (type=="plotView")
    {
      # should check that plotType, dfRows and dfColumns came in the ... 
      # parameter - need to just send any other parameters from ... 
      paramList<-list(...)
      if (all(c("plotType","dfRows","dfColumns") %in% names(paramList)))
        newView<-createPlotView(win,dataName,...)
    }  
    win<-win(newView)

    assign("activeView",newView,viewEnv)

    # need to add a callback for focus_in_event
    gtkAddCallback(win,"focus_in_event",
      function(obj,ev)
      {
        if (is(newView,"plotView"))
        {
          # set the active device
          dev.set(plotDevice(newView))
        }
        # set the active view
        assign("activeView",newView,viewEnv)
      }
    )

    # add the key press events for pseudo-accelerators
    createKeyPressEvent(win)        

    # add the delete event
    closeWin(newView)

    # add a new sub menu item to the control window
    winCounter<-get("winCounter", controlEnv)
    assign("winCounter", winCounter+1, controlEnv)

    curLabel<-paste("Window _", get("winCounter",controlEnv),
                    "  Ctrl+", get("winCounter",controlEnv),sep="")
    newMenuItem<-addSubMenuItem("window", curLabel, "activateWindow",  
                   winNum=get("winCounter",controlEnv))
    winNum(newView)<-get("winCounter",controlEnv)

    # set the title of the window
    curTitle<-paste("Window ", get("winCounter",controlEnv),sep="")
    gtkWindowSetTitle(win, curTitle)

    # add new object to viewList      
    viewList<-get("viewList",viewEnv)
    viewLen<-length(viewList)
    viewList[[viewLen+1]]<-newView
    assign("viewList",viewList,viewEnv)
  }
}

###########
# createDataView creates a gtk window that 
# contains a spreadsheet of the data from one of the
# loaded data sets.
###########
createDataView<-function(win, dfName)
{        
  win$SetUsize(500,400)
  newTable<-gtkTable(1,1,TRUE)

  # actually get the data
  showdata<-getData(type="dataframe",name=dfName)
  # need to add one to include the row names as a column
  numCols<-(dim(showdata)[2])+1
  numRows<-dim(showdata)[1]
  # need to add a frame that shows the data
  frData<-gtkFrame(dfName)     

  boxForShow<-gtkHBox(TRUE,2)
  scShow<-gtkScrolledWindow()
  scShow$SetPolicy("automatic", "automatic")

  showDfList<-gtkCList(numCols,titles=c("Row.Names",names(showdata)))
  showDfList$Freeze()
  for (i in 1:numRows)
  {
    text<-c(row.names(showdata)[i],as.matrix(showdata[i,]))
    showDfList$Append(text)
  }
  showDfList$Thaw()
  # make the column titles just be labels rather than buttons
  gtkCListColumnTitlesPassive(showDfList)
  # also want to set the column width so all items in the column
  # can be seen
  gtkCListColumnsAutosize(showDfList)
  # set the selection mode to multiple so multiple items can be selected
  # at a time (not sure if I want extended)
  gtkCListSetSelectionMode(showDfList,GtkSelectionMode[3])

  scShow$AddWithViewport(showDfList)
  boxForShow$PackStart(scShow,expand=TRUE)
  frData$Add(boxForShow)
  gtkTableAttach(newTable,frData,0,1,0,1,ypadding=10,xpadding=5)
  win$Add(newTable)
  win$Show()

  # need to store the window
  curView<-new("spreadView",dataName=dfName,win=win,clist=showDfList)
 
  gtkAddCallback(showDfList,"select-row",
    function(obj,row,col,ev)
    {
      if (get("clickEvent", controlEnv))
        clickEvent(curView,row.names(showdata)[(row+1)],event="select")
    }
  )
   
  gtkAddCallback(showDfList,"unselect-row",
    function(obj,row,col,ev)
    {
      if (get("clickEvent", controlEnv))
        clickEvent(curView,row.names(showdata)[(row+1)],event="unselect")
    }
  )

  return(curView)
}

#########
# create plot views
#########
createPlotView<-function(win, dfName, plotType, ...)
{
  # this will create a gtk device on the window
  retList<-createGtkDev(win)
  win<-retList$win
  drArea<-retList$drArea

  if (plotType=="sPlotView")
  {
    # make sure dfRows and dfColumns are in ... parameter
    newView<-createSPlotView(win,dfName,drArea,...)
  }

  return(newView)
}

##########
# create a sPlotView object, make a scatterplot and add callbacks
##########
createSPlotView<-function(win, dfName, drArea, dfRows, dfColumns)
{
  curView<-new("sPlotView", dataName=dfName, win=win, 
                    plotDevice=dev.cur(), plotPar=par(no.readonly=TRUE),
                    drArea=drArea, dfRows=dfRows, colx=dfColumns[1], 
                    coly=dfColumns[2])

  curView<-scatterplot(curView)

  gtkAddCallback(drArea(curView),"button_press_event",
    function(obj,ev)
    {
      clickEvent(curView,ev)
    }
  )
      
  gtkAddCallback(drArea(curView),"motion-notify-event",
    function(obj,ev)
    {
      motionEvent(curView,ev)
    }
  )

  return(curView)
}

#################
# scatterplot is a function to plot a data set
# it is used so that the viewList in viewEnv can be updated
# dfName is the data set name to plot, DFrows are the rows to use
# DFcolumns are the columns to use, dev is the device number
# 
# viewItem is a sPlotView object
#################
scatterplot<-function(viewItem)
{
  dfName<-dataName(viewItem)
  DFrows<-dfRows(viewItem)
  DFcolumns<-c(colx(viewItem),coly(viewItem))  
  dev<-plotDevice(viewItem)

  # make sure the length of DFcolumns is 2
  curDF<-getData(type="dataframe",name=dfName)
  plotDF<-curDF[DFrows,DFcolumns]

  dev.set(dev)

  # get the highlit column
  curHigh<-curDF[DFrows,match("highlit",names(curDF))]
  curHide<-curDF[DFrows,match("hide",names(curDF))]

  # what about the data stored in the DF columns: color, highlit, pch, hide
  paramList<-setParamList(curDF,DFrows,viewItem)  

  # it seems like the window needs to be shown before plotting or I
  # get a gdk error
  win<-win(viewItem)
  win$Show()

  # set a few parameters that don't seem to be set correctly for a gtk device
  par("bg"="transparent")
  par("col"="black")
  par("fg"="black")

  # couldn't put in the parameter list directly 
  # note: may have more than one point here and this can cover up the change
  plot(plotDF,col=paramList$col,pch=paramList$pch)

  # need to highlight points
  highPoints<-plotDF[curHigh,]
  if (nrow(highPoints) > 0)
  {
    pointCex<-plotPar(viewItem)$cex
    curCex<-as.integer(pointCex)+1
    points(highPoints[,1], highPoints[,2], cex=curCex, 
           col=get("highColor",dataEnv))
  }
 
  # need to hide points
  hidePoints<-plotDF[curHide,]
  if (nrow(hidePoints) > 0)
  {
    backgr<-plotPar(viewItem)$bg
    if (backgr=="transparent")
      backgr="white"
    # make sure to fill in the point as background in case it is colored in
    points(hidePoints[,1], hidePoints[,2], col=backgr, pch=19)
  }

  hideAndHigh<-curHigh & curHide
  if (any(hideAndHigh))
  {
    hideAndHighPoints<-plotDF[hideAndHigh,]
    points(hideAndHighPoints[,1], hideAndHighPoints[,2], cex=curCex,
            col=backgr)
  }

  # add a title
  title(get("viewMode",controlEnv), col.main=get("mainTitleColor",viewEnv))
  assign("plotTitle", get("viewMode",controlEnv), controlEnv)

  # need to update plot parameter usr
  plotPar(viewItem)<-par(no.readonly=TRUE)

  return(viewItem)
}
environment(scatterplot)<-viewEnv

#################
# replot is a function that tells the plot to replot itself
# plotIndex is the list index (in viewList) of the plot that needs
# to be replotted
#################
replot<-function(plotIndex)
{
  # redraw this plot
  viewItem<-viewList[[plotIndex]]

  # set the device before resetting parameters
  dev<-plotDevice(viewItem)
  dev.set(dev)

  # reset the plot parameters
  par(plotPar(viewItem))

  # replot and get the plot data
  curView<-scatterplot(viewItem)

  # assign this new item in viewList
  viewList[[plotIndex]]<-curView
  assign("viewList",viewList,viewEnv)
}
environment(replot)<-viewEnv

################
# createGtkDev is a function to create a Gtk device for plotting
# -also adds a callback for the focus event
################
createGtkDev<-function(w)
{
  dev <- c()

  w$SetUsize(500, 500)

  # need to add rulers
  vbox<-gtkVBox()
  hrul<-gtkHRuler(show=FALSE)
  hrul$SetMetric("pixels")
  hrul$SetRange(0, 100, 0, 10)
  gtkBoxPackStart(vbox,hrul,expand=FALSE)

  hbox<-gtkHBox()
  gtkBoxPackStart(vbox,hbox)
  vrul<-gtkVRuler(show=FALSE)
  vrul$SetMetric("pixels")
  vrul$SetRange(0, 100, 0, 10)
  gtkBoxPackStart(hbox,vrul,expand=FALSE)

  w$Add(vbox)
 
  drArea <- gtkDrawingArea()
  dev<-addDrawingArea(hbox, drArea, 250, 250, dev, devName="Dev1", 
                      addType=5)

  # want to be informed of these events on the device
  drArea$SetEvents(c("pointer-motion-mask", "pointer-motion-hint-mask", 
                   "button-press-mask", "button-release-mask", 
                   "key-press-mask", "key-release-mask"))

  return(list(win=w,drArea=drArea))
}
environment(createGtkDev)<-viewEnv

########
# how to update the data set if the current view is a scatterplot
# expects where to be a point (a list)
########
updateDFBysPlot<-function(viewObj, where)
{
  dfList<-get("dfList",dataEnv)
  plotDfName<-dataName(viewObj)

  # now need to get data based on viewMode
  cData<-getChangeDFData(viewObj,where)

  return(list(type="modify",mData=cData,to=plotDfName))
}

##########
# how to update the dataframe if the current view is a spreadsheet
# expects where to be a row name (not a point)
##########
updateDFBySpread<-function(viewObj, where, ...)
{
  event<-list(...)$event
  viewMode<-get("viewMode",controlEnv)
  dfName<-dataName(viewObj)
  actualDF<-get("dfList",dataEnv)[[dfName]][["curDF"]]

  if (viewMode=="identify" || viewMode=="highlight")
  {      
    if (event=="select")
      cData<-list(dfName=dataName(viewObj), Rname=where, Cname="highlit",
                  data=as.list(TRUE))
    if (event=="unselect")
      cData<-list(dfName=dataName(viewObj), Rname=where, Cname="highlit",
                  data=as.list(FALSE))
  }
  if (viewMode=="color")
  {
    if (event=="select")
    {
      curColor<-get("currentColor", controlEnv)

      datalist<-vector(mode="list", length=2)
      datalist[1]<-curColor
      datalist[2]<-19

      cData<-list(dfName=dataName(viewObj), Rname=c(where,where), 
                  Cname=c("color","pch"), data=datalist)
    }
    if (event=="unselect")
    {
      # don't want to change the row's values
      curColor<-actualDF[match(where, row.names(actualDF)), 
                           match("color", names(actualDF))]
      datalist<-vector(mode="list",length=1)
      datalist[1]<-curColor

      cData<-list(dfName=dataName(viewObj), Rname=c(where), 
                  Cname=c("color"), data=datalist)
    }
  }
  if (viewMode=="hide")
  {
    if (event=="select")
      cData<-list(dfName=dataName(viewObj), Rname=where, Cname="hide",
                  data=as.list(TRUE))
    if (event=="unselect")
      cData<-list(dfName=dataName(viewObj), Rname=where, Cname="hide",
                  data=as.list(FALSE))
  }

  return(list(type="modify",mData=cData,to=dataName(viewObj)))  
}

######
# updatePlots is called by handleMessage method for a gUpdateViewMessage object
######
updatePlots<-function(type,plotIndex,data)
{
  activeDev<-dev.cur() 

  if (activeDev != 1)
  {
    # to update scatterplot views
    updateSPlots(type,plotIndex,data)
    # can add another function to update a different plot view

    # reset the active device after all plots have been updated
    dev.set(activeDev)
  }
}

#########
# update only scatterplot views
#########
updateSPlots<-function(type,plotIndex,data)
{
  # update plots
  if (length(plotIndex) > 0)
    for (i in 1:length(plotIndex))
    {
      if (type=="updatePoints")
        do.call(type,list(plotIndex=plotIndex[i],Rname=data$Rname))
      else
        do.call(type,list(plotIndex=plotIndex[i]))
    }
}

#########
# updateSpread is called by handleMessage for a gUpdateViewMessage object
# will only be updated one spreadsheet at a time because have only
# one spreadsheet per data set
#########
updateSpread<-function(curDataName,spreadIndex,data)
{
  # just updating the data view - don't want the click event to occur
  assign("clickEvent", FALSE, controlEnv)

  viewList<-get("viewList",viewEnv)

  # make sure that there is a spreadsheet to update
  if (length(spreadIndex) > 0)
  { 
    curSpreadView<-viewList[[spreadIndex]]

    # check if only one row of the spreadsheet needs to be updated
    if (length(data) > 0)
    {
      # this implementation depends on the data being a dataframe
      # can call a new function if want to get data from a class other
      # than dataframe
      retList<-getRowDataFromDF(dfName=curDataName,data=data)
      rowData<-retList$rowData
      rowIndex<-retList$rowIndex

      # now put the updated data in the spreadsheet
      curClist<-clist(curSpreadView)
      curClist$Freeze()

      # assuming the data to be put in the spreadsheet will be either
      # in matrix or dataframe form
      for (i in 1:nrow(rowData))
      {
        # just reset the text for that row - could be slow with double loop
        for (j in 1:ncol(rowData))
        {
          gtkCListSetText(curClist,rowIndex[i]-1,j,rowData[i,j])
        }
      }
      curClist$Thaw()

      # need to select or unselect the appropriate row
      viewMode<-get("viewMode",controlEnv)
      if (viewMode == "highlight" || viewMode=="identify")
      {
        for (i in 1:nrow(rowData))
        {
          highVal<-rowData[i,match("highlit",names(rowData))]
          if (highVal)
            gtkCListSelectRow(curClist,rowIndex[i]-1,-1)
          else
            gtkCListUnselectRow(curClist,rowIndex[i]-1,-1)
        }
      }
      if (viewMode=="color")
      {
        for (i in 1:nrow(rowData))
        {
          gtkCListSelectRow(curClist,rowIndex[i]-1,-1)
        }
      }
      if (viewMode=="hide")
      {
        for (i in 1:nrow(rowData))
        {
          hideVal<-rowData[i,match("hide",names(rowData))]
          if (hideVal)
            gtkCListSelectRow(curClist,rowIndex[i]-1,-1)
          else
            gtkCListUnselectRow(curClist,rowIndex[i]-1,-1)
        } 
      }
    }

    # need to redo the whole window because reset was performed
    else
    {
      curWin<-win(curSpreadView)
      gtkContainerRemove(curWin,gtkContainerGetChildren(curWin)[[1]])
      retList<-createDataView(curWin,curDataName)

      win(curSpreadView)<-win(retList$newView)
      clist(curSpreadView)<-clist(retList$newView)
      viewList[[spreadIndex]]<-curSpreadView
      assign("viewList",viewList,viewEnv)
    }
  }

  # reset the value
  assign("clickEvent", TRUE, controlEnv)
}

########
# only get a row of the dataframe for updating a spreadsheet
########
getRowDataFromDF<-function(dfName,data)
{
  dataF<-getData(type="dataframe",name=dfName)
  
  # just want a row from the dataframe
  # rowname is either the name or index of the row
  rowIndex<-data$Rname
  allRowNames<-row.names(dataF) 

  # may have more than one row
  if (!all(is.numeric(rowIndex)))
    rowIndex<-match(rowIndex,allRowNames)

  return(list(rowIndex=rowIndex,rowData=dataF[rowIndex,]))
}

######
# make an interface for getting data, in case new
# data types are added besides dataframe or matrix
######
getData<-function(type,name,...)
{
  if (type=="dataframe")
  {
    dfList<-get("dfList",dataEnv)
    dataF<-dfList[[name]][["curDF"]]
    curData<-dataF
  }
  # can add extra data types here!!!

  # assume that whatever data is returned can have the following 
  # functions performed on it: row.names(), names(), []
  # i.e. the same functions that can be performed on a dataframe

  return(curData)
}

#########
# an interface to update data
#########
updateData<-function(type,name,newData,...)
{
  if (type=="dataframe")
  {
    dfList<-get("dfList",dataEnv)
    dfList[[name]][["curDF"]]<-newData
    assign("dfList",dfList,dataEnv)
  }
  # can add new data types here!
}

##########
# sets the focus on a window
##########
activateWindow<-function(winNum)
{
#  print(winNum)
  if (winNum==0)
  {
    controlWin<-get("controlWin",controlEnv)
    if (length(controlWin) > 0)
    {
#      gtkWindowActivateFocus(controlWin)
      gtkObjectSignalEmit(controlWin,"hide")
      gtkObjectSignalEmit(controlWin,"show")
    }
  }
  else
  {
    viewList<-get("viewList",viewEnv)
    if (length(viewList) > 0)
    {
      curWinNums<-unlist(lapply(viewList, function(x) {winNum(x)}))
      curWins<-lapply(viewList, function(x) {win(x)})
      index<-match(winNum,curWinNums) 
      if (!is.na(index))
      {
        gtkObjectSignalEmit(curWins[[index]],"hide")
        gtkObjectSignalEmit(curWins[[index]],"show")
#        gtkWindowActivateFocus(curWins[[index]])        
      }
    }
  }
}




###########
# create a class for views
###########
setClass("genView", representation(dataName="character", win="GtkWindow",
         winNum="numeric"), contains=("VIRTUAL"))

setClass("plotView", representation(plotDevice="numeric", plotPar="list",
         drArea="GtkDrawingArea"), contains="genView")
setClass("sPlotView", representation(dfRows="numeric", colx="numeric", 
         coly="numeric"), contains="plotView")

setClass("spreadView", representation(clist="GtkCList"), 
         contains="genView")

#####
# accessor functions
#####
if (is.null(getGeneric("dataName")))
  setGeneric("dataName", function(object)
            standardGeneric("dataName"))
setMethod("dataName", "genView", function(object)
         object@dataName)

if (is.null(getGeneric("win")))
  setGeneric("win", function(object)
            standardGeneric("win"))
setMethod("win", "genView", function(object)
         object@win)

if (is.null(getGeneric("winNum")))
  setGeneric("winNum", function(object)
            standardGeneric("winNum"))
setMethod("winNum", "genView", function(object)
         object@winNum)

if (is.null(getGeneric("plotDevice")))
  setGeneric("plotDevice", function(object)
            standardGeneric("plotDevice"))
setMethod("plotDevice", "plotView", function(object)
         object@plotDevice)

if (is.null(getGeneric("plotPar")))
  setGeneric("plotPar", function(object)
            standardGeneric("plotPar"))
setMethod("plotPar", "plotView", function(object)
         object@plotPar)

if (is.null(getGeneric("drArea")))
  setGeneric("drArea", function(object)
            standardGeneric("drArea"))
setMethod("drArea", "plotView", function(object)
         object@drArea)

if (is.null(getGeneric("dfRows")))
  setGeneric("dfRows", function(object)
            standardGeneric("dfRows"))
setMethod("dfRows", "sPlotView", function(object)
         object@dfRows)

if (is.null(getGeneric("colx")))
  setGeneric("colx", function(object)
            standardGeneric("colx"))
setMethod("colx", "sPlotView", function(object)
         object@colx)

if (is.null(getGeneric("coly")))
  setGeneric("coly", function(object)
            standardGeneric("coly"))
setMethod("coly", "sPlotView", function(object)
         object@coly)

if (is.null(getGeneric("clist")))
  setGeneric("clist", function(object)
            standardGeneric("clist"))
setMethod("clist", "spreadView", function(object)
         object@clist)

#####
# setting the slots
#####
if (is.null(getGeneric("dataName<-")))
  setGeneric("dataName<-",function(object,value)
            standardGeneric("dataName<-"))
setReplaceMethod("dataName","genView",function(object,value)
         {
           object@dataName<-value
           object
         }
)

if (is.null(getGeneric("win<-")))
  setGeneric("win<-",function(object,value)
            standardGeneric("win<-"))
setReplaceMethod("win","genView",function(object,value)
         {
           object@win<-value
           object
         }
)

if (is.null(getGeneric("winNum<-")))
  setGeneric("winNum<-",function(object,value)
            standardGeneric("winNum<-"))
setReplaceMethod("winNum","genView",function(object,value)
         {
           object@winNum<-value
           object
         }
)

if (is.null(getGeneric("plotDevice<-")))
  setGeneric("plotDevice<-",function(object,value)
            standardGeneric("plotDevice<-"))
setReplaceMethod("plotDevice","plotView",function(object,value)
         {
           object@plotDevice<-value
           object
         }
)

if (is.null(getGeneric("plotPar<-")))
  setGeneric("plotPar<-",function(object,value)
            standardGeneric("plotPar<-"))
setReplaceMethod("plotPar","plotView",function(object,value)
         {
           object@plotPar<-value
           object
         }
)

if (is.null(getGeneric("drArea<-")))
  setGeneric("drArea<-",function(object,value)
            standardGeneric("drArea<-"))
setReplaceMethod("drArea","plotView",function(object,value)
         {
           object@drArea<-value
           object
         }
)

if (is.null(getGeneric("dfRows<-")))
  setGeneric("dfRows<-",function(object,value)
            standardGeneric("dfRows<-"))
setReplaceMethod("dfRows","sPlotView",function(object,value)
         {
           object@dfRows<-value
           object
         }
)

if (is.null(getGeneric("colx<-")))
  setGeneric("colx<-",function(object,value)
            standardGeneric("colx<-"))
setReplaceMethod("colx","sPlotView",function(object,value)
         {
           object@colx<-value
           object
         }
)

if (is.null(getGeneric("coly<-")))
  setGeneric("coly<-",function(object,value)
            standardGeneric("coly<-"))
setReplaceMethod("coly","sPlotView",function(object,value)
         {
           object@coly<-value
           object
         }
)

if (is.null(getGeneric("clist<-")))
  setGeneric("clist<-",function(object,value)
            standardGeneric("clist<-"))
setReplaceMethod("clist","spreadView",function(object,value)
         {
           object@clist<-value
           object
         }
)

#####
# generic functions for gtk events on view objects
#####
if (is.null(getGeneric("motionEvent")))
  setGeneric("motionEvent", function(object, event,...)
            standardGeneric("motionEvent"))

if (is.null(getGeneric("clickEvent")))
  setGeneric("clickEvent", function(object, where, ...)
            standardGeneric("clickEvent"))

if (is.null(getGeneric("viewUpdateData")))
  setGeneric("viewUpdateData", function(object, where, ...)
            standardGeneric("viewUpdateData"))

##########
# update the data based on interacting with a spreadsheet view
##########
setMethod("viewUpdateData", "spreadView",
  function(object, where, ...)
  {
    retList<-updateDFBySpread(object, where, ...)
    return(retList)
  }
)

setMethod("viewUpdateData", "sPlotView",
  function(object, where, ...)
  {
    retList<-updateDFBysPlot(object, where)
    return(retList)
  }
)

########
# clickEvent for spreadView is called in response to a selected row
# or unselected row from the clist on a spreadView
########
setMethod("clickEvent", "spreadView", 
  function(object, where, ...)
  {
    cont<-TRUE
    extras<-list(...)
    event<-extras$event
    viewMode<-get("viewMode",controlEnv)
    if (viewMode == "")
      cont<-FALSE
    # we don't need to update the data if the view mode is color and a
    # row has been unselected
    if (viewMode=="color" && event=="unselect")
      cont<-FALSE

    # only update the data if view mode is set
    if (cont)
    {
      # where will be the list item that was clicked 
      dfMessage<-new("gUpdateDataMessage", from=object, where=where, ...)
      handleMessage(dfMessage)
    }
  }
)

#########
# create a button press event for scatterplot views (used to be
# addButtonPress function)
#########
setMethod("clickEvent", "sPlotView",
  function(object, where, ...)
  {
    # only update the data if view mode is set and if the view mode is not
    # identify - in identify mode only pay attention to motion event
    viewMode<-get("viewMode", controlEnv)
    if (viewMode != "" && viewMode != "identify")
    {
      #where will be the mouse click event information from Gtk
    
      # check that the left button was pressed
      if (gdkEventButtonGetButton(where)==1)
      {
        usri <- pix2inches(where[["X"]], where[["Y"]])
        usrc <- inches2usr(usri$x, usri$y)

#        viewMode<-get("viewMode",controlEnv)

        curPoint<-identifyPoint(object,usrc)

        if (!is.null(curPoint))
        {
          # if a point was clicked, then we want to create a 
          # gUpdateDataMessage
          dfMessage<-new("gUpdateDataMessage", from=object, where=curPoint)
          handleMessage(dfMessage)
        }
      }
      # may want to add functionality if the right or middle 
      # buttons are pressed

      # for right click have a widget pop up that asks the 
      # user which interact function to use??
      if (gdkEventButtonGetButton(where)==3)
      {
        
      }
    }
  }
)

########
# a method for a motion notify event on a scatterplot
########
setMethod("motionEvent", "sPlotView",
  function(object,event,...)
  {
    # only respond to motion events for identify view mode
    if (get("viewMode",controlEnv)=="identify")
    {
      curWin<-win(object)
      # potential problem - these depend on how the 
      # scatterplot window is set up
      # SHOULD AT LEAST CHECK THEIR CLASSES!!
      hrul<-gtkContainerGetChildren(gtkContainerGetChildren(curWin)[[1]])[[1]]
      vrul<-gtkContainerGetChildren(gtkContainerGetChildren(
             gtkContainerGetChildren(curWin)[[1]])[[2]])[[1]]

      hrul$SignalEmit("motion-notify-event",event)
      vrul$SignalEmit("motion-notify-event",event)
      
      curx<-gdkEventMotionGetX(event)
      cury<-gdkEventMotionGetY(event)

      # also need to ensure that the plot has been added to viewList
      curDev<-dev.cur()
      plotDev<-getPlotDev(getPlotsFromViews())
      if (curDev %in% plotDev)
      {
        checkPoint(curx,cury,object)
      }
    }
  }
)


###########
# create a class for messages
###########
# a virtual class for message
setClass("gMessage")

setClass("gUpdateMessage", representation(type="character", mData="list"),
         contains="gMessage")
setClass("gUpdateViewMessage", contains="gUpdateMessage")
setClass("gUpdateDataMessage", representation(to="character"), 
         contains="gUpdateMessage")

setClass("gAddMessage", representation(dataName="character", mData="list"), 
         contains="gMessage")
setClass("gAddViewMessage", representation(type="character"), 
         contains="gAddMessage")
setClass("gAddDataMessage", contains="gAddMessage")

#####
# accessor functions
#####
if (is.null(getGeneric("type")))
  setGeneric("type", function(object)
            standardGeneric("type"))
setMethod("type", "gUpdateMessage", function(object)
         object@type)
setMethod("type", "gAddViewMessage", function(object)
         object@type)

if (is.null(getGeneric("mData")))
  setGeneric("mData", function(object)
            standardGeneric("mData"))
setMethod("mData", "gUpdateMessage", function(object)
         object@mData)
setMethod("mData", "gAddMessage", function(object)
         object@mData)

if (is.null(getGeneric("to")))
  setGeneric("to", function(object)
            standardGeneric("to"))
setMethod("to", "gUpdateDataMessage", function(object)
         object@to)

if (is.null(getGeneric("dataName")))
  setGeneric("dataName", function(object)
            standardGeneric("dataName"))
setMethod("dataName", "gAddMessage", function(object)
         object@dataName)

#####
# setting the slots
#####
if (is.null(getGeneric("type<-")))
  setGeneric("type<-",function(object,value)
            standardGeneric("type<-"))
setReplaceMethod("type","gUpdateMessage",function(object,value)
         {
           object@type<-value
           object
         }
)
setReplaceMethod("type","gAddViewMessage", function(object,value)
         {
           object@type<-value
           object
         }
)

if (is.null(getGeneric("mData<-")))
  setGeneric("mData<-",function(object,value)
            standardGeneric("mData<-"))
setReplaceMethod("mData","gUpdateMessage",function(object,value)
         {
           object@mData<-value
           object
         }
)
setReplaceMethod("mData", "gAddMessage", function(object,value)
         {
           object@mData<-value
           object
         }
)

if (is.null(getGeneric("to<-")))
  setGeneric("to<-",function(object,value)
            standardGeneric("to<-"))
setReplaceMethod("to","gUpdateDataMessage",function(object,value)
         {
           object@to<-value
           object
         }
)

if (is.null(getGeneric("dataName<-")))
  setGeneric("dataName<-",function(object,value)
            standardGeneric("dataName<-"))
setReplaceMethod("dataName","gAddMessage",function(object,value)
         {
           object@dataName<-value
           object
         }
)

#####
# initialize methods
#####
setMethod("initialize", "gUpdateViewMessage", 
  function(.Object, type, ...)
  {
    # type is the type of change that was performed on the data (currently,
    # the data can only be dataframes)
    if (type=="reset")
    {
      .Object@type <- "replot"
      .Object@mData <- list()
    }
    if (type=="modify")
    {
      Rname<-list(...)$Rname
      .Object@type <- "updatePoints"
      .Object@mData <- list(Rname=Rname)
    }
    .Object
  }
)

#####
# have the 
# any type of view can update a dataframe - should this be 
# even more general???
#####
setMethod("initialize", "gUpdateDataMessage", 
  function(.Object, from, where, ...)
  {
    # put the update dataframe in a separate function in case the user
    # ever wants to update a different type of data, then it will be easy
    # to add a new function in place of updateDF
    if (is(from,"genView"))
      retList<-viewUpdateData(from, where, ...)

    .Object@type<-retList$type
    .Object@mData<-retList$mData
    .Object@to<-retList$to
    
    .Object
  }
)

setMethod("initialize", "gAddDataMessage",
  function(.Object, data, ...)
  {
    extraParam<-list(...)
    if (length(extraParam) > 0)
      dataName<-extraParam$dataName
    else
    {
      dataName<-data
      data<-eval(as.name(data))
    }      

    .Object@dataName<-dataName
    .Object@mData<-list(data=data)
    
    .Object
  }
)

setMethod("initialize", "gAddViewMessage",
  function(.Object, dataName, type, ...)
  {
    .Object@mData<-list(...)
    .Object@dataName<-dataName
    .Object@type<-type

    .Object
  }
)

#####
# generic functions for handling messages
#####
if (is.null(getGeneric("handleMessage")))
  setGeneric("handleMessage", function(object,...)
            standardGeneric("handleMessage"))

setMethod("handleMessage", "gUpdateViewMessage",
  function(object,...)
  {
    # expect the data name to be in the ... parameter
    curDataName<-list(...)$dataName 

    # first determine which plots need to be updated
    viewList<-get("viewList",viewEnv)
    allDataNames<-unlist(lapply(viewList, function(x) {dataName(x)}))
    booPlots<-unlist(lapply(viewList, function(x) {is(x,"plotView")}))
    booSpread<-unlist(lapply(viewList, function(x) {is(x,"spreadView")}))

    # the indices in the viewList of the scatterplots and spreadsheet that
    # depend on the data name
    plotIndex<-which(booPlots & (allDataNames %in% curDataName))
    spreadIndex<-which(booSpread & (allDataNames %in% curDataName))

    type<-type(object)
    data<-mData(object)

    updatePlots(type,plotIndex,data) 

    updateSpread(curDataName,spreadIndex,data)    
  }
)

setMethod("handleMessage", "gUpdateDataMessage",
  function(object,...)
  {
    data<-mData(object)
    name<-to(object)
    type<-type(object)

    # could add a different function to change a different type of data

    # this function changes a dataframe  
    do.call("modify",data)
 
    # update the views now that the data has changed
    dfName<-data$dfName
    Rname<-data$Rname
    if (length(get("viewList",viewEnv)) > 0)
      updateViews(dfName,type,Rname)
  }
)

setMethod("handleMessage", "gAddDataMessage",
  function(object,...)
  {
    curList<-mData(object)
    data<-curList$data
    name<-dataName(object)

    loadDFData(data,name)
    checkIfPlotted(name)
  }
)
 
setMethod("handleMessage", "gAddViewMessage",
  function(object,...)
  {
    dataName<-dataName(object)
    type<-type(object)
    mData<-mData(object)

    mData$dataName<-dataName
    mData$type<-type
   
    do.call("createView",mData)
#    createView(type=type, dataName=dataName, mData)    
  }
)












# an environment for the controller
controlEnv<-new.env()

#############
# setControlEnvDefaults is set when the control window is
# loaded or closed so that controlEnv variables are
# set to the defaults
#############
setControlEnvDefaults<-function()
{
  setControlWindowDefaults()

  assign("currentColor","black",controlEnv)

  # store the color browser window if it is opened
  assign("colorBrowser",c(),controlEnv)

  assign("viewMode","",controlEnv)
  assign("plotTitle","",controlEnv)

  # for when we want to fill in a point
  assign("fillPch",19,controlEnv)

  # for numbering the views
  assign("winCounter",0,controlEnv)

  assign("curAction","",controlEnv)

  # when a row is selected, need to know if the click event should occur
  assign("clickEvent",TRUE,controlEnv)
}

#############
# setControlWindowDefaults is normally called by setControlEnvDefaults.
# The only time it will be called by itself is if the user closes
# the control window without going through the Quit menu item.  That
# means that plots may still be open and thus, not all control
# environment variables should be reset to their default values.  Only
# the control environment variables that are in this function should
# be reset to their defaults when the user closes only the control window.
#############
setControlWindowDefaults<-function()
{
  # for adding menu items to the main menu
  # ag is the accelerator group
  assign("ag",c(),controlEnv)
  # menuBar is the main menu bar (all menus are added to the menu bar)
  assign("menuBar",c(),controlEnv)
  # menus is the list of menus (such as file, edit, display, ...)
  assign("menus",list(),controlEnv)
  # need to store the key values for the menu item accelerators
  assign("keyVals",list(),controlEnv)
  # to store the window menu item - needed to update the menu
  assign("windowItem",c(),controlEnv)

  # for determining an available data frame to load
  assign("availDF","",controlEnv)
  # for determining which dataframe to show
  assign("showDF","",controlEnv)

  # to assign which variables are X and Y
  assign("chosenX","",controlEnv)
  assign("chosenY","",controlEnv)

  # controlWin is the gtkWindow
  assign("controlWin",c(),controlEnv)

  # need to store the last action - used to refresh control window
  assign("oldAction","",controlEnv)

  # for loading a dataframe
  assign("dataF","",controlEnv)

  # used when searching for a point by row name
  assign("findInfo","",controlEnv)
  assign("findOn","",controlEnv)

  # vBox is the vertical box that contains the main menu and table
  assign("vBox",c(),controlEnv)
  # store the table so it can be added and removed from vBox on
  # w (the gtkWindow)
  assign("gtkTable",c(),controlEnv)

  assign("notDone",TRUE,controlEnv)

  # to choose X/Y variables for plotting
  assign("toggleButtonXList",list(),controlEnv)
  assign("toggleButtonYList",list(),controlEnv)

  assign("winDefaultHeight",500,controlEnv)
  assign("winDefaultWidth",300,controlEnv)
}

#############
# createControlWindow creates a gtk window that controls
# what happens when interacting with a plot or when linking
# plots
#############
createControlWindow<-function()
{
  # check to ensure that a control window is not already open
  if (length(ls(env=controlEnv)) > 0)
  {
    oldWin<-get("controlWin",controlEnv)
  }
  else
  {
    setControlEnvDefaults()
    oldWin<-get("controlWin",controlEnv)
  }

  if (length(oldWin)==0)
  {
    w<-gtkWindow(show=FALSE)

    # the vertical box will never be removed from the window, but
    # the table will be removed depending on which menu item is
    # chosen (table will be added later)
    vBox<-gtkVBoxNew()
    assign("vBox",vBox,controlEnv)
    assign("controlWin",w,controlEnv)

    menuBar<-createMainMenu()

    # need to create boxes or frames
    gtkBoxPackStart(vBox,menuBar,expand=FALSE,fill=FALSE)

    gtkContainerAdd(w,vBox)

    gtkObjectSetArgs(w,"allow_shrink"=TRUE)
    gtkObjectSetArgs(w,"allow_grow"=TRUE)
    gtkObjectSetArgs(w,"auto_shrink"=TRUE)
    gtkContainerSetResizeMode(w,0)

    w$SetUsize(get("winDefaultHeight",controlEnv),
               get("winDefaultWidth",controlEnv))

    w$Show()

    # need a delete event callback in case the user closes
    # the window without using the Quit menu item
    gtkAddCallback(w,"delete_event",
      function(obj,ev)
      {
        setControlWindowDefaults()
        w$Destroy()
      }
    )
  }
  invisible(NULL)
}

#############
# createMainMenu creates a top level menu for
# the control window
#
# note: don't need to pass the window because it can
# be accessed through the environment variable, controlWin
#############
createMainMenu<-function()
{
  # list of menus to store in controlEnv
  menus<-list()

  ag<-gtkAccelGroup()
  win<-get("controlWin",controlEnv)
  gtkAccelGroupAttach(ag,win)
  assign("ag",ag,controlEnv)

  #following the code in gtk.org Menu example
  # want to see if callbacks work
  fileMenu<-gtkMenuNew()
  editMenu<-gtkMenuNew()
  displayMenu<-gtkMenuNew()
  viewModeMenu<-gtkMenuNew()

  # added the control part to the label because it wasn't automatically
  # doing it using the GtkAccelFlags like it's supposed to (I think)
  openFItem<-createMenuItem("_Open File  Ctrl+O", ag, "activate",
                GdkModifierType[3], GtkAccelFlags[2], "openf")
  openDItem<-createMenuItem("Open _Data  Ctrl+D", ag, "activate",
                GdkModifierType[3], GtkAccelFlags[2], "opend")
  quitItem<-createMenuItem("_Quit      Ctrl+Q", ag, "activate",
                GdkModifierType[3], GtkAccelFlags[2], "quit")

  findItem<-createMenuItem("_Find    Ctrl+F", ag, "activate",
                GdkModifierType[3], GtkAccelFlags[2], "find")
  replotItem<-createMenuItem("_Replot   Ctrl+R", ag, "activate",
                GdkModifierType[3], GtkAccelFlags[2], "replot")
  resetItem<-createMenuItem("Re_set   Ctrl+S", ag, "activate",
                GdkModifierType[3], GtkAccelFlags[2], "reset")

  viewDataItem<-createMenuItem("_View Data      Ctrl+V", ag, "activate",
                GdkModifierType[3], GtkAccelFlags[2], "viewData")
  plotItem<-createMenuItem("_Plot Data     Ctrl+P", ag, "activate",
                GdkModifierType[3], GtkAccelFlags[2], "plotD")

  colorItem<-createMenuItem("_Color     Ctrl+C", ag, "activate",
                GdkModifierType[3], GtkAccelFlags[2], "color")
  highlightItem<-createMenuItem("_Highlight   Ctrl+H", ag, "activate",
                GdkModifierType[3], GtkAccelFlags[2], "highlight")
  identifyItem<-createMenuItem("_Identify    Ctrl+I", ag, "activate",
                GdkModifierType[3], GtkAccelFlags[2], "identify")
  hideItem<-createMenuItem("Hid_e     Ctrl+E", ag, "activate",
                GdkModifierType[3], GtkAccelFlags[2], "hide")

  gtkMenuAppend(fileMenu,openDItem)
  gtkMenuAppend(fileMenu,openFItem)
  gtkMenuAppend(fileMenu,quitItem)

  gtkMenuAppend(editMenu,findItem)
  gtkMenuAppend(editMenu,replotItem)
  gtkMenuAppend(editMenu,resetItem)

  gtkMenuAppend(displayMenu,viewDataItem)
  gtkMenuAppend(displayMenu,plotItem)

  gtkMenuAppend(viewModeMenu,colorItem)
  gtkMenuAppend(viewModeMenu,highlightItem)
  gtkMenuAppend(viewModeMenu,identifyItem)
  gtkMenuAppend(viewModeMenu,hideItem)

  menuBar<-gtkMenuBarNew()
  gtkWidgetShow(menuBar)

  # GdkModifierType[4] is alt
  fileItem<-createMenuItem("_File", ag, "activate-item",
                           GdkModifierType[4], GtkAccelFlags[2])

  editItem<-createMenuItem("_Edit", ag, "activate-item",
                           GdkModifierType[4], GtkAccelFlags[2])

  displayItem<-createMenuItem("_Display", ag, "activate-item",
                           GdkModifierType[4], GtkAccelFlags[2])

  viewModeItem<-createMenuItem("_ViewMode", ag, "activate-item",
                           GdkModifierType[4], GtkAccelFlags[2])

  windowItem<-createMenuItem("_Window", ag, "activate-item",
                           GdkModifierType[4], GtkAccelFlags[2])

  assign("windowItem", windowItem, controlEnv)

  gtkMenuItemSetSubmenu(fileItem,fileMenu)
  gtkMenuItemSetSubmenu(editItem,editMenu)
  gtkMenuItemSetSubmenu(displayItem,displayMenu)
  gtkMenuItemSetSubmenu(viewModeItem,viewModeMenu)

  gtkMenuBarAppend(menuBar,fileItem)
  gtkMenuBarAppend(menuBar,editItem)
  gtkMenuBarAppend(menuBar,displayItem)
  gtkMenuBarAppend(menuBar,viewModeItem)
  gtkMenuBarAppend(menuBar,windowItem)

  menus[["file"]]<-fileMenu
  menus[["edit"]]<-editMenu
  menus[["display"]]<-displayMenu
  menus[["viewMode"]]<-viewModeMenu
  assign("menus",menus,controlEnv)

  assign("menuBar",menuBar,controlEnv)

  addWindowMenu()

  return(menuBar)
}

#####
# addWindowMenu is used after calling gtkMenuItemRemoveSubmenu(windowItem)
# to reset the window menu
#####
addWindowMenu<-function()
{
  windowMenu<-gtkMenuNew()

  windowItem<-get("windowItem",controlEnv)

  menus<-get("menus",controlEnv)
  menus[["window"]]<-windowMenu
  assign("menus",menus,controlEnv)

  controlWinItem<-addSubMenuItem("window",  "Control _Window  Ctrl+W",
                                 "activateWindow", winNum=0)

  viewList<-get("viewList",viewEnv)

  # already have a menu item for each element in viewList
  if (length(viewList) > 0)
  {
    for (i in 1:length(viewList))
    {
      curLabel<-paste("Window _", winNum(viewList[[i]]),
                    "  Ctrl+", winNum(viewList[[i]]), sep="")
      newMenuItem<-addSubMenuItem("window", curLabel, "activateWindow",
                   winNum=winNum(viewList[[i]]))
    }
  }
  gtkMenuItemSetSubmenu(windowItem,windowMenu)

  menus<-get("menus",controlEnv)
  menus[["window"]]<-windowMenu
  assign("menus",menus,controlEnv)
}

############
# create one menu item with an accelerator and possibly, a callback
############
createMenuItem<-function(labelText, ag, method, modType,
                         accelFlag, curAction="", ...)
{
  newItem<-gtkMenuItemNew()
  newLabel<-gtkAccelLabelNew(labelText)
  newKey<-gtkLabelParseUline(newLabel,labelText)

  gtkWidgetAddAccelerator(newItem, method, ag, newKey, modType,
                          accelFlag)
  gtkContainerAdd(newItem,newLabel)
  gtkWidgetShow(newItem)

  if (method=="activate")
  {
    gtkAddCallback(newItem,"activate",
      function(obj)
      {
        updateCurAction(curAction, ...)
      }
    )
  }

  # get the accelerator character
  labelSplit<-unlist(strsplit(labelText,"_"))
  if (length(labelSplit) > 1)
    accelChar<-substr(labelSplit[2],1,1)
  else
    accelChar<-""

  keyVals<-get("keyVals", controlEnv)
  lenKV<-length(keyVals)
  keyVals[[lenKV+1]]<-list(keyval=newKey, action=curAction,
                           accelChar=accelChar, ...)
  assign("keyVals", keyVals, controlEnv)

  return(newItem)
}

#############
# addMenuItem adds a menu item to the main menu after the main menu
# has already been created
# meant to be used if the user wants to add a menu item from the
# command line
# ##FIXME: but it would be better if all code went through here, then
# ##you don't have one system for you and another for the users
#
#
# menuName is the name the menu will take in the list menus, which
# is stored in controlEnv (so the menu can be accessed again)
#############
addMenuItem<-function(menuName, labelText, ag=get("ag",controlEnv),
                      method="activate-item", modType=GdkModifierType[4],
                      accelFlag=GtkAccelFlags[2])
{
  ##check to see if it is there
  if( !is.null(controlEnv$menus[[menuName]]) )
      stop(paste(menuName, "is already in the menu"))

  newItem<-createMenuItem(labelText, ag, method, modType, accelFlag)
  menuBar<-get("menuBar",controlEnv)
  gtkMenuBarAppend(menuBar,newItem)
  gtkWidgetShow(menuBar)

  # make a sub menu for this new menu item (so things can be added to
  # it later)
  newMenu<-gtkMenuNew()
  gtkMenuItemSetSubmenu(newItem,newMenu)

  # need to also add the new menu item to menus list
  controlEnv$menus[[menuName]] = newMenu
}

getItemLabel = function(x) {
    if( !inherits(x, "GtkMenuItem") )
        stop("must have a GtkMenuItem")
    gtkLabelGet(gtkContainerGetChildren(x)[[1]])
}

getMenuItemLabels = function(x) {
    if( !inherits(x, "GtkMenu") )
        stop("must have a GtkMenu")
    v1 = gtkContainerGetChildren(x)
    sapply( v1, getItemLabel)
}

##FIXME: needs to be hidden by a namespace
.hasItem = function(menu, itemName) {
    if( !inherits(menu, "GtkMenu") || !is.character(itemName) ||
    length(itemName) != 1)
        stop("wrong argument")
    itemLabs = getMenuItemLabels(menu)
    if( length(itemLabs)==0 )
        return(numeric(0))
    grep(paste("^", itemName, sep=""), itemLabs)
}

##############
# addSubMenuItem is to add a sub menu item to an already existing menu
# first add the menu item using addMenuItem and then add sub menu items
# using this function (add the menu item if you don't want to use file,
# edit, display or viewMode)
#
# menuName is the name of the menu that this sub menu item will be added to
# this must match the name of one of the list items in menus in controlEnv
# curAction should be the function to call when this item is activated
# note that this function cannot have any parameters (see updateControlWindow)
##############
addSubMenuItem<-function(menuName, labelText, action,
                       ag=get("ag",controlEnv), method="activate",
                       modType=GdkModifierType[3], accelFlag=GtkAccelFlags[2],
                       ...)
{
  # should check that the accelerator is not already in use!!
  ##first check to see if labelText is already there

  curMenu<- controlEnv$menus[[menuName]]
  if( is.null(curMenu) )
      stop(paste(menuName, "is not a menu item"))

  hasLabel = .hasItem(curMenu, labelText)
  if( length(hasLabel) > 0 )
      stop("item", hasLabel, "has the same label")

  newItem<-createMenuItem(labelText=labelText, ag=ag, method=method,
           modType=modType, accelFlag=accelFlag, curAction=action, ...)

  gtkMenuAppend(curMenu,newItem)
  return(invisible(newItem))
}

###########
# update the current action environment variable and call update control window
# to reflect the change in current action
###########
updateCurAction<-function(curAct, ...)
{
  assign("curAction", curAct, controlEnv)
  updateControlWindow(...)
}

##############
# updateControlWindow updates the control window
# after a different menu item was selected
##############
updateControlWindow<-function(...)
{
  curAction<-get("curAction",controlEnv)

  # decide how to update based on curAction
  # problem it just keeps adding to the window (unless use table)

  # note that the default is to think that curAction is a function that
  # has no parameters (changed from setEmptyView)
  switch(curAction,
    "openf"=setLoadView(),
    "opend"=setOpenDView(),
    "plotD"=setPlotDView(),
    "viewData"=setViewDataView(),
    "quit"=askToQuit(),
    "identify"=setIdentifyMode(),
    "highlight"=setHighlightMode(),
    "hide"=setHideMode(),
    "replot"=askToReplot(),
    "reset"=askToReset(),
    "find"=askToFind(),
    "color"=setColorMode(),
    "empty"=setEmptyView(),
    do.call(curAction,list(...))
  )
}

##########
# getPlotsFromViews returns all objects of type plotView from viewList
##########
getPlotsFromViews<-function()
{
  viewList<-get("viewList",viewEnv)
  curPlots<-unlist(lapply(viewList, function(x) {is(x,"plotView")}))
  if (any(curPlots))
    return(viewList[curPlots])
  else
    return(list())
}

##########
# getSpreadFromViews returns all objects of type spreadView from viewList
##########
getSpreadFromViews<-function()
{
  viewList<-get("viewList",viewEnv)
  curSpread<-unlist(lapply(viewList, function(x) {is(x,"spreadView")}))
  if (any(curSpread))
    return(viewList[curSpread])
  else
    return(list())
}

#########
# ask the user which point they want to find
#########
askToFind<-function()
{
  # make sure that there are views to find something on and a view mode
  viewMode<-get("viewMode",controlEnv)

  if (length(get("viewList",viewEnv)) > 0 && viewMode != "")
  {
    curView<-get("activeView",viewEnv)
    dfName<-dataName(curView)

    curDF<-getData(type="dataframe",name=dfName)
    rowNames<-row.names(curDF)

    removeCol<-names(get("defaultPlotData",dataEnv))
    actualData<-curDF[,!(names(curDF) %in% removeCol)]

    if (is(curView,"sPlotView"))
    {
      colx<-colx(curView)
      coly<-coly(curView)
    }

    # need to check that the user entered a valid row name
    getFindInfo()
    curText<-get("findInfo",controlEnv)
    findOn<-get("findOn",controlEnv)

    if (findOn=="Row" && curText %in% rowNames)
    {
      if (is(curView, "sPlotView"))
      {
        # need to get the x,y values for that row
        rowIndex<-match(curText,rowNames)
        xval<-curDF[rowIndex,colx]
        yval<-curDF[rowIndex,coly]
        curPoint<-list(closestXY=list(x=xval,y=yval),closestPoint=curText)

        # create a gUpdateDataMessage
        dfMessage<-new("gUpdateDataMessage", from=curView, where=curPoint)
      }
      if (is(curView, "spreadView"))
        dfMessage<-new("gUpdateDataMessage", from=curView, where=curText,
                       event="select")
      handleMessage(dfMessage)
    }
    if (findOn=="Data Element")
    {
      # check if the curText is a data element
      matchVals<-apply(actualData, 2, function(x)
           {
             # to get rid of leading white spaces
             x <- gsub("^[[:space:]]+", "", x)
             # to get rid of tailing white spaces
             x <- gsub("[[:space:]]+$", "", x)
             match(curText, x)
            })
      if (!all(is.na(matchVals)))
      {
        minMatch<-min(matchVals,na.rm=TRUE)
        if (is(curView, "sPlotView"))
        {
          # just pick a point from that row
          xval<-curDF[minMatch,colx]
          yval<-curDF[minMatch,coly]
          curPoint<-list(closestXY=list(x=xval,y=yval),
                       closestPoint=rowNames[minMatch])

          # create a gUpdateDataMessage
          dfMessage<-new("gUpdateDataMessage", from=curView, where=curPoint)
        }
        if (is(curView, "spreadView"))
          dfMessage<-new("gUpdateDataMessage", from=curView,
                          where=rowNames[minMatch], event="select")
        handleMessage(dfMessage)
      }
    }
  }
  # let the user know that a plot must be open
  else
  {
    w<-gtkWindow(show=FALSE)
    lbl<-gtkLabel("There is no row to find because there \nare no views or the view mode is not set.")
    but<-gtkButton("Ok")
    vbox<-gtkVBoxNew()
    hbox<-gtkHBoxNew()
    w$Add(vbox)
    gtkBoxPackStart(vbox,lbl)
    gtkBoxPackStart(vbox,hbox)
    gtkBoxPackStart(hbox,but,padding=60)
    w$Show()
    gtkAddCallback(but,"clicked",
      function(obj)
      {
        w$Destroy()
      }
    )
  }
}

#########
# open a window to get the string (row name) to find
#########
getFindInfo<-function()
{
  assign("notDone",TRUE,controlEnv)
  win<-gtkWindow(show=FALSE)
  win$SetUsize(250,100)
  gtkWindowSetModal(win,TRUE)

  tab<-gtkTableNew(3,5,TRUE)
  vbox<-gtkVBoxNew()

  gtkBoxPackStart(vbox,tab)

  lbl<-gtkLabel("Find based on: ")

  gtkTableAttach(tab,lbl,0,2,0,1,xpadding=1,ypadding=5)
  comb<-gtkCombo()
  gtkComboSetPopdownStrings(comb,c("Row","Data Element"))
  gtkComboSetValueInList(comb,TRUE,FALSE)
  gtkTableAttach(tab,comb,2,5,0,1,xpadding=1,ypadding=5)

  lblVal<-gtkLabel("Value: ")
  gtkTableAttach(tab,lblVal,0,2,1,2,xpadding=1,ypadding=5)
  ent<-gtkEntry()
  gtkTableAttach(tab,ent,2,5,1,2,xpadding=1,ypadding=5)
  okBut<-gtkButton("Ok")
  okBut$SetUsize(50,20)
  gtkTableAttach(tab,okBut,2,4,2,3,xpadding=1,ypadding=5)
  win$Add(vbox)

  gtkAddCallback(okBut,"clicked",
    function(obj)
    {
      findOn<-gtkEntryGetText(gtkContainerGetChildren(comb)[[1]])
      curText<-gtkEntryGetText(ent)
      assign("findInfo",curText,controlEnv)
      assign("findOn",findOn,controlEnv)
      assign("notDone",FALSE,controlEnv)
      win$Destroy()
    }
  )

  win$Show()

  # need to make sure that notDone is set to FALSE if they user
  # closes the window without using the Ok button
  gtkAddCallback(win,"delete_event",
    function(obj,ev)
    {
      assign("notDone",FALSE,controlEnv)
    }
  )

  # to make the get find info window modal
  while (get("notDone",controlEnv))
    Sys.sleep(0.5)
}

##########
# open a window to check that the user wants to reset the dataframe
##########
askToReset<-function()
{
  if (length(get("viewList",viewEnv)) > 0)
  {
    win<-gtkWindow(show=FALSE)
    win$SetUsize(300,60)
    vBox<-gtkVBoxNew()
    lab<-gtkLabel("Do you want to reset the data to its original state?")
    hBox<-gtkHBoxNew()
    gtkBoxPackStart(vBox,lab,padding=5)
    gtkBoxPackStart(vBox,hBox)
    yesBut<-gtkButton("Yes")
    yesBut$SetUsize(40,15)
    noBut<-gtkButton("No")
    noBut$SetUsize(40,15)
    gtkBoxPackEnd(hBox,yesBut,expand=FALSE,fill=FALSE,padding=5)
    gtkBoxPackEnd(hBox,noBut,expand=FALSE,fill=FALSE,padding=5)
    win$Add(vBox)
    gtkAddCallback(yesBut,"clicked",
      function(obj)
      {
        # resetting a dataframe based on the active plot so first
        # make sure that some plots exist
        activeView<-get("activeView",viewEnv)
        dfName<-dataName(activeView)

        dfList<-get("dfList",dataEnv)
        origData<-dfList[[dfName]][["origDF"]]
        datIndex<-match(dfName,names(dfList))
        # remove this data
        dfList[datIndex]<-c()
        assign("dfList",dfList,dataEnv)

        # create a gAddDataMessage to add the data back
        dfMessage<-new("gAddDataMessage", data=origData, dataName=dfName)
        handleMessage(dfMessage)

        win$Destroy()
      }
    )
    gtkAddCallback(noBut,"clicked",
      function(obj)
      {
        win$Destroy()
      }
    )
    win$Show()
  }

  # let the user know that no resetting was done
  else
  {
    w<-gtkWindow(show=FALSE)
    lbl<-gtkLabel("There is no data to reset \nbecause there are no plots.")
    but<-gtkButton("Ok")
    vbox<-gtkVBoxNew()
    hbox<-gtkHBoxNew()
    w$Add(vbox)
    gtkBoxPackStart(vbox,lbl)
    gtkBoxPackStart(vbox,hbox)
    gtkBoxPackStart(hbox,but,padding=60)
    w$Show()
    gtkAddCallback(but,"clicked",
      function(obj)
      {
        w$Destroy()
      }
    )
  }
}

##########
# open a window to check that the user wants to replot the dataframe
##########
askToReplot<-function()
{
  if (length(get("viewList",viewEnv)) > 0)
  {
    win<-gtkWindow(show=FALSE)
    win$SetUsize(250,60)
    vBox<-gtkVBoxNew()
    lab<-gtkLabel("Do you want to replot the data?")
    hBox<-gtkHBoxNew()
    gtkBoxPackStart(vBox,lab,padding=5)
    gtkBoxPackStart(vBox,hBox)
    yesBut<-gtkButton("Yes")
    yesBut$SetUsize(40,15)
    noBut<-gtkButton("No")
    noBut$SetUsize(40,15)
    gtkBoxPackEnd(hBox,yesBut,expand=FALSE,fill=FALSE,padding=5)
    gtkBoxPackEnd(hBox,noBut,expand=FALSE,fill=FALSE,padding=5)
    win$Add(vBox)
    gtkAddCallback(yesBut,"clicked",
      function(obj)
      {
        # must have an active view to replot
        activeView<-get("activeView",viewEnv)
        dfName<-dataName(activeView)

        # 7-7-04 now will update all views when replot
        # want to only update the plots - no changes will occur to the data
        # make the type "reset" so the plots are replotted
        updateViews(dfName,type="reset")
        win$Destroy()
      }
    )
    gtkAddCallback(noBut,"clicked",
      function(obj)
      {
        win$Destroy()
      }
    )
    win$Show()
  }

  # let the user know that there was no plot to replot
  else
  {
    w<-gtkWindow(show=FALSE)
    lbl<-gtkLabel("There is no view to replot.")
    but<-gtkButton("Ok")
    vbox<-gtkVBoxNew()
    hbox<-gtkHBoxNew()
    w$Add(vbox)
    gtkBoxPackStart(vbox,lbl)
    gtkBoxPackStart(vbox,hbox)
    gtkBoxPackStart(hbox,but,padding=40)
    w$Show()
    gtkAddCallback(but,"clicked",
      function(obj)
      {
        w$Destroy()
      }
    )
  }
}

###############
# askToQuit opens a widget that asks the user
# if they really want to quit when the user has
# chosen quit on the main menu
#
# this function should close all windows if the
# user chooses yes!!!
###############
askToQuit<-function()
{
  win<-gtkWindow(show=FALSE)
  win$SetUsize(250,60)
  vBox<-gtkVBoxNew()
  lab<-gtkLabel("Are you sure you want to quit?")
  hBox<-gtkHBoxNew()
  gtkBoxPackStart(vBox,lab,padding=5)
  gtkBoxPackStart(vBox,hBox)
  yesBut<-gtkButton("Yes")
  yesBut$SetUsize(40,15)
  noBut<-gtkButton("No")
  noBut$SetUsize(40,15)
  gtkBoxPackEnd(hBox,yesBut,expand=FALSE,fill=FALSE,padding=5)
  gtkBoxPackEnd(hBox,noBut,expand=FALSE,fill=FALSE,padding=5)
  win$Add(vBox)
  gtkAddCallback(yesBut,"clicked",
    function(obj)
    {
      # call a function to destroy all windows
      clearAllWindows()
      win$Destroy()
    }
  )
  gtkAddCallback(noBut,"clicked",
    function(obj)
    {
      win$Destroy()
    }
  )
  win$Show()
}

#############
# clearAllWindows destroys all windows stored in viewEnv
# and controlEnv
#############
clearAllWindows<-function()
{
  viewList<-get("viewList",viewEnv)
  viewLen<-length(viewList)
  if (viewLen > 0)
    for(i in 1:viewLen)
    {
      # note: also need to close the control window!!!
      #setDeleteEvents is stored in linkedPlots.R
      setDeleteEvents(viewList[[i]])
    }

  # need to remove the control window
  cWin<-get("controlWin",controlEnv)
  if (length(cWin) > 0)
  {
    cWin$Destroy()
    # reset the control window to empty
    assign("controlWin",c(),controlEnv)
  }

  # check if the color browser is open
  cBrowser<-get("colorBrowser",controlEnv)
  if (length(cBrowser) > 0)
  {
    cBrowser$Destroy()
    assign("colorBrowser",c(),controlEnv)
  }

  # set the control environment variables to their defaults
  setControlEnvDefaults()
}

###########
# resetWinSize resets the gtkWindow to its default size
# (having trouble with this)
###########
resetWinSize<-function()
{
  w<-get("controlWin",controlEnv)
  w$SetUsize(get("winDefaultHeight",controlEnv),
             get("winDefaultWidth",controlEnv))
}

#############
# setEmptyView sets the view on the control window to
# only have the main menu
#############
setEmptyView<-function()
{
  vbox<-get("vBox",controlEnv)
  tabl<-get("gtkTable",controlEnv)

  # remove the table from the control window
  if (length(tabl)>0)
    gtkContainerRemove(vbox,tabl)

  # reset the table to empty
  assign("gtkTable",c(),controlEnv)

  assign("dataF","",controlEnv)

  # reset the chosen X and Y to unknown
  assign("chosenX","",controlEnv)
  assign("chosenY","",controlEnv)

  # since the control window view is being changed, we want
  # to store this action as oldAction;
  # only update old action if the action is one that changes the
  # control window - and any time the control window is being
  # changed, setEmptyView is called
  oldAction<-get("curAction", controlEnv)
  assign("oldAction", oldAction, controlEnv)
}

##############
# setPlotDView sets the control window so it
# shows the view of loaded dataframes to
# choose which one to plot
##############
setPlotDView<-function()
{
  w <- controlEnv$controlWin

  # reset values before opening new view
  if (length(get("gtkTable",controlEnv)) > 0)
    setEmptyView()
  else
    # make sure old action gets set when the first window is loaded
    assign("oldAction",get("curAction",controlEnv),controlEnv)

  tab<-gtkTableNew(8,8,TRUE)
  vbox<-get("vBox",controlEnv)

  gtkBoxPackStart(vbox,tab)
  assign("gtkTable",tab,controlEnv)

  loadedDF<-getLoadedDF()

  frLoaded<-gtkFrame("Loaded Data")
  boxForLoaded<-gtkVBox(TRUE,2)
  sc<-gtkScrolledWindow()
  sc$SetPolicy("automatic", "automatic")
  loadedDfList<-gtkList()
  if (length(loadedDF)>0)
    for (i in 1:length(loadedDF))
    {
      loadedDfListItem<-gtkListItemNewWithLabel(loadedDF[i])
      loadedDfList$Add(loadedDfListItem)
    }

  gtkListSetSelectionMode(loadedDfList,1)
  sc$AddWithViewport(loadedDfList)
  boxForLoaded$PackStart(sc,expand=TRUE)
  frLoaded$Add(boxForLoaded)
  gtkTableAttach(tab,frLoaded,0,4,0,6,xpadding=10)

  frVar<-gtkFrame("Data Variables")
  gtkTableAttach(tab,frVar,4,8,0,6,xpadding=10)

  gtkAddCallback(loadedDfList,"select-child",
    function(obj,objchild)
    {
      # reset chosen X and Y because now looking at new dataframe
      assign("chosenX","",controlEnv)
      assign("chosenY","",controlEnv)

      # need to the active element in the list
      selItem<-gtkChildren(objchild)[[1]][["label"]]
      assign("dataF",selItem,controlEnv)

      setVariables(selItem,frVar)
    }
  )

  plotBut<-gtkButton("Plot")
  gtkTableAttach(tab, plotBut, 3, 5, 6, 8, xpadding=20, ypadding=20)

  gtkAddCallback(plotBut,"clicked",
    function(obj,ev)
    {
      # need to get the two plot variables and call plotting function
      chosenX<-get("chosenX",controlEnv)
      chosenY<-get("chosenY",controlEnv)
      if (chosenX != "" && chosenY != "")
      {
        # then need to call plotting function
        dataF<-get("dataF",controlEnv)
        curDF<-getData(type="dataframe",name=dataF)
        varNames<-names(curDF)
        numRows<-dim(curDF)[1]
        Xindex<-match(chosenX,varNames)
        Yindex<-match(chosenY,varNames)
        DFrows<-1:numRows
        DFcolumns<-c(Xindex,Yindex)

        # create a message to create a view
        vMessage<-new("gAddViewMessage", dataName=dataF, type="plotView",
                  plotType="sPlotView", dfRows=DFrows, dfColumns=DFcolumns)
        handleMessage(vMessage)
      }
    }
  )
}

#############
# setViewDataView allows the user to view the data
#############
setViewDataView<-function()
{
  # reset values before opening new view
  if (length(get("gtkTable",controlEnv)) > 0)
    setEmptyView()
  else
    # make sure old action gets set when the first window is loaded
    assign("oldAction",get("curAction",controlEnv),controlEnv)

  tab<-gtkTableNew(4,4,TRUE)
  vbox<-get("vBox",controlEnv)

  gtkBoxPackStart(vbox,tab)
  assign("gtkTable",tab,controlEnv)

  loadedDF<-getLoadedDF()

  frAvail<-gtkFrame("Available Data")
  boxForAvail<-gtkHBox(TRUE,2)
  sc<-gtkScrolledWindow()
  sc$SetPolicy("automatic", "automatic")
  availDfList<-gtkList()
  if (length(loadedDF) > 0)
    for (i in 1:length(loadedDF))
    {
      availDfListItem<-gtkListItemNewWithLabel(loadedDF[i])
      availDfList$Add(availDfListItem)
    }

  gtkListSetSelectionMode(availDfList,1)
  sc$AddWithViewport(availDfList)
  boxForAvail$PackStart(sc,expand=TRUE)
  frAvail$Add(boxForAvail)
  gtkTableAttach(tab,frAvail,0,2,0,4,ypadding=10,xpadding=5)

  gtkAddCallback(availDfList,"select-child",
    function(obj,objchild)
    {
      # need to get the active element in the list
      selItem<-gtkChildren(objchild)[[1]][["label"]]
      assign("showDF",selItem,controlEnv)
    }
  )

  showButton<-gtkButton("Show")
  gtkTableAttach(tab,showButton,2,4,1,3,ypadding=50,xpadding=70)

  gtkAddCallback(showButton,"clicked",
    function(obj)
    {
      showDF<-get("showDF",controlEnv)

      if (showDF != "")
      {
        # create and handle a message to create a view
        vMessage<-new("gAddViewMessage", dataName=showDF, type="spreadView")
        handleMessage(vMessage)
      }
    }
  )
}

#############
# setOpenDView allows the user to choose a data set to open
#############
setOpenDView<-function()
{
  w<-get("controlWin",controlEnv)

  # reset values before opening new view
  if (length(get("gtkTable",controlEnv)) > 0)
    setEmptyView()
  else
    # make sure old action gets set when the first window is loaded
    assign("oldAction",get("curAction",controlEnv),controlEnv)

  tab<-gtkTableNew(8,8,TRUE)
  vbox<-get("vBox",controlEnv)

  gtkBoxPackStart(vbox,tab)
  assign("gtkTable",tab,controlEnv)

  # note that objects other than dataframes are returned from getData
  availDF<-getDataFromData()
  loadedDF<-getLoadedDF()
  # remove any dataframes from availDF that are already loaded
  if (!is.null(loadedDF))
    availDF<-availDF[!(availDF %in% loadedDF)]

  frAvail<-gtkFrame("Available Data")
  boxForAvail<-gtkVBox(TRUE,2)
  sc<-gtkScrolledWindow()
  sc$SetPolicy("automatic", "automatic")
  availDfList<-gtkList()
  if (length(availDF) > 0)
    for (i in 1:length(availDF))
    {
      availDfListItem<-gtkListItemNewWithLabel(availDF[i])
      availDfList$Add(availDfListItem)
    }

  gtkListSetSelectionMode(availDfList,1)
  sc$AddWithViewport(availDfList)
  boxForAvail$PackStart(sc,expand=TRUE)
  frAvail$Add(boxForAvail)
  gtkTableAttach(tab,frAvail,0,3,0,8,ypadding=10)

  frLoaded<-gtkFrame("Loaded Data")
  boxForLoaded<-gtkVBox(TRUE,2)
  scLoad<-gtkScrolledWindow()
  scLoad$SetPolicy("automatic", "automatic")
  loadedDfList<-gtkList()
  if (length(loadedDF) > 0)
    for (i in 1:length(loadedDF))
    {
      loadedDfListItem<-gtkListItemNewWithLabel(loadedDF[i])
      loadedDfList$Add(loadedDfListItem)
    }

  gtkListSetSelectionMode(loadedDfList,1)
  scLoad$AddWithViewport(loadedDfList)
  boxForLoaded$PackStart(scLoad,expand=TRUE)
  frLoaded$Add(boxForLoaded)
  gtkTableAttach(tab,frLoaded,5,8,0,8,ypadding=10)

  gtkAddCallback(availDfList,"select-child",
    function(obj,objchild)
    {
      # need to get the active element in the list
      selItem<-gtkChildren(objchild)[[1]][["label"]]
      # remove spaces from the text
      #selItem<-unlist(strsplit(selItem, " "))[1]
      assign("availDF",selItem,controlEnv)
    }
  )

  loadBut<-gtkButton("Load")
  gtkTableAttach(tab, loadBut, 3, 5, 3, 5, xpadding=20, ypadding=20)

  gtkAddCallback(loadBut, "clicked",
    function(obj)
    {
      loadIfDF()
    }
  )
}

#############
# only load if a dataframe or matrix
#############
loadIfDF<-function()
{
  curDF<-get("availDF",controlEnv)

  if (curDF != "")
  {

    # added for R-2.0.0
    # now need to break into 2 pieces if there are parentheses
    splitCurDf<-unlist(strsplit(curDF," "))

    if (length(splitCurDf) > 1)
    {
      # need to remove parentheses from second element
      removeParen<-splitCurDf[2]
      removeParen<-substr(removeParen,2,nchar(removeParen)-1)
      dataParem<-removeParen
      dataName<-splitCurDf[1]
    }
    else
    {
      dataParem<-splitCurDf[1]
      dataName<-splitCurDf[1]
    }

#    data(list=curDF)
    data(list=dataParem)

    # now check to make sure it has been loaded
#    if (!is.na(match(curDF,ls(.GlobalEnv))))
    if (!is.na(match(dataName,ls(.GlobalEnv))))
    {
      # only load dataframes or matrices
#      if (is.data.frame(eval(as.name(curDF))) ||
#          is.matrix(eval(as.name(curDF))) )
      if (is.data.frame(eval(as.name(dataName))) ||
          is.matrix(eval(as.name(dataName))) )
      {
#         dMessage<-new("gAddDataMessage", data=curDF)
         dMessage<-new("gAddDataMessage", data=dataName)
         handleMessage(dMessage)

        # refresh the view or add/remove the item
        setOpenDView()
      }
      else
      {
        # let the user know that the data is not a dataframe
#        showNotDF(curDF)
        showNotDF(dataName)
      }
    }
    assign("availDF","",controlEnv)
  }
}

###############
# let the user know that the data was not a dataframe or matrix
###############
showNotDF<-function(curDF)
{
  win<-gtkWindow(show=FALSE)
  hbox<-gtkHBox()
  win$Add(hbox)
  lbl<-gtkLabel(paste(curDF,"is not a data frame or a \nmatrix so it could ",
                      "not be loaded."))
  gtkBoxPackStart(hbox,lbl,padding=5)
  okBut<-gtkButton("Ok")
  okBut$SetUsize(40,20)
  gtkBoxPackStart(hbox,okBut,padding=5)

  gtkAddCallback(okBut,"clicked",
    function(obj)
    {
      win$Destroy()
    }
  )

  win$Show()
}

############
# getData returns everything from data()
############
getDataFromData<-function()
{
  curData<-data()
  curDF<-curData$results[,match("Item",dimnames(curData$results)[[2]])]
  scurDF<-sort(curDF)

#  # problem with this is that I can only tell the classes of data
#  # in the package, datasets
#  # now check their classes - ADDED 10/6/2004
#  testClass<-rep(FALSE,length(scurDF))
#  for (i in 1:length(scurDF))
#  {
#    curDFName<-unlist(strsplit(scurDF[i], " "))[1]
#    if (is.matrix(eval(as.name(curDFName))) ||
#        is.data.frame(eval(as.name(curDFName))))
#      testClass[i]<-TRUE
#  }
#  # now return only those that are true for data frame or matrix
#  scurDF<-scurDF[testClass]

  return(scurDF)
}

###############
# getDataFramesInEnv returns all data.frames that are
# in the environment, env
###############
getDataFramesInEnv<-function(env=".GlobalEnv")
{
  allEnv<-search()
  index<-grep(env,allEnv)
  getAllObjects<-ls(index)

  datFr<-rep(FALSE,length(getAllObjects))
  for (i in 1:length(getAllObjects))
  {
    if (is.data.frame(eval(as.name(getAllObjects[i]))))
      datFr[i]<-TRUE
  }

  # now know how many data.frames are in the environment, env
  if (any(datFr)==TRUE)
    datFrInEnv<-getAllObjects[datFr]
  else
    datFrInEnv<-c()

  return(sort(datFrInEnv))
}

##########
# setVariables sets the dataframe variables in a list box
#
# dataF is the dataframe name, frVar is the frame that
# will hold the dataframe variables
##########
setVariables<-function(dataF,frVar)
{
  # need to reset the button boxes before adding anything
  resetVarButtonBoxes(frVar)

  # need to add the button boxes along with the variable labels
  varNames<-names(getData(type="dataframe",name=dataF))

  # remove the variable names that pertain to default plot data
  defaultPlotNames<-names(get("defaultPlotData",dataEnv))
  remNames<-(varNames %in% defaultPlotNames)
  varNames<-sort(varNames[!remNames])

  boxForVar<-gtkVBox(TRUE,2)
  sc<-gtkScrolledWindow()
  sc$SetPolicy("automatic", "automatic")

  XbuttonList<-list()
  YbuttonList<-list()

  if (length(varNames) > 0)
  {
    # length(varNames)=# of rows, 3 columns, homogenous=FALSE??
    tab<-gtkTable(length(varNames),3,FALSE)

    for (i in 1:length(varNames))
    {
      XbuttonList[[i]]<-gtkToggleButton("X")
#      XbuttonList[[i]]$SetUsize(10,10)
      YbuttonList[[i]]<-gtkToggleButton("Y")
#      YbuttonList[[i]]$SetUsize(10,10)
      lab<-gtkLabel(varNames[i])
#      lab$SetUsize(10,10)
      gtkTableAttach(tab,XbuttonList[[i]],0,1,i-1,i,xpadding=5)
      gtkTableAttach(tab,YbuttonList[[i]],1,2,i-1,i,xpadding=5)
      gtkTableAttach(tab,lab,2,3,i-1,i,xpadding=5)

      XbuttonList[[i]]$AddCallback("clicked",
        substitute(setToggleX(j,dataFr),list(j=i,dataFr=dataF)))
      YbuttonList[[i]]$AddCallback("clicked",
        substitute(setToggleY(j,dataFr),list(j=i,dataFr=dataF)))
    }
    sc$AddWithViewport(tab)
  }
  assign("toggleButtonXList",XbuttonList,controlEnv)
  assign("toggleButtonYList",YbuttonList,controlEnv)

  boxForVar$PackStart(sc,expand=FALSE)

  frVar$Add(boxForVar)
}

############
# setToggleX sets the chosen X value
#
# j is the index of the button that just changed state
# dataFr is the current dataframe
############
setToggleX<-function(j,dataFr)
{
  setToggleInactive(j,"X",dataFr)
}

##############
# setToggleY sets the chosen Y value
#
# j is the index of the button that just changed state
# dataFr is the current dataframe
##############
setToggleY<-function(j,dataFr)
{
  setToggleInactive(j,"Y",dataFr)
}

##############
# setToggleInactive sets the toggle buttons so
# only one button can be active at a time and it
# also sets the chosenX/chosenY values for plotting
#
# j is the index of the button that just changed state
# type is X or Y
# dataFr is the current dataframe
##############
setToggleInactive<-function(j,type,dataFr)
{
  # need to check if there are two buttons active
  XbuttonList<-get("toggleButtonXList",controlEnv)
  YbuttonList<-get("toggleButtonYList",controlEnv)

  indexActive<-0
  numActive<-0
  for (i in 1:length(XbuttonList))
  {
    if (type=="X")
    {
      if (gtkToggleButtonGetActive(XbuttonList[[i]])==TRUE)
      {
        numActive<-numActive+1
        indexActive<-i
      }
    }
    else
    {
      if (gtkToggleButtonGetActive(YbuttonList[[i]])==TRUE)
      {
        numActive<-numActive+1
        indexActive<-i
      }
    }
  }

  if (numActive > 1)
    for (i in 1:length(XbuttonList))
    {
      if (i != j)
      {
        if (type=="X")
        {
          gtkToggleButtonSetActive(XbuttonList[[i]],FALSE)
        }
        else
        {
          gtkToggleButtonSetActive(YbuttonList[[i]],FALSE)
        }
      }
    }

  # if there is only one active toggle button - set that to be chosen
  else
  {
    # get the variable names
    varNames<-names(getData(type="dataframe",name=dataFr))

    # remove the variable names that pertain to default plot data
    defaultPlotNames<-names(get("defaultPlotData",dataEnv))
    remNames<-(varNames %in% defaultPlotNames)
    varNames<-sort(varNames[!remNames])

    if (type=="X")
      assign("chosenX",varNames[indexActive],controlEnv)
    else
      assign("chosenY",varNames[indexActive],controlEnv)
  }
}

###########
# resetVarButtonBoxes removes all children from the frVar
#
# frVar is the frame containing the button boxes for the
# dataframe variables
###########
resetVarButtonBoxes<-function(frVar)
{
  # remove all items from the frame
  kids<-gtkContainerGetChildren(frVar)
  if (length(kids) > 0)
    for (i in 1:length(kids))
    {
      gtkWidgetDestroy(kids[[i]])
    }
}

##############
# setLoadView opens a file explorer, which
# allows the user to load a file
# if the file loads a dataframe or a matrix, that data set
# is added to dfList in dataEnv
##############
setLoadView<-function()
{
  fileSel<-gtkFileSelection(title="Open a File",show=FALSE)
  # need to set the callbacks on these buttons
  okBut<-gtkFileSelectionGetOkButton(fileSel)
  canBut<-gtkFileSelectionGetCancelButton(fileSel)
  # need to hide 3 file buttons
  gtkFileSelectionHideFileopButtons(fileSel)

  fileSel$Show()

  gtkAddCallback(canBut,"clicked",
    function(obj)
    {
      fileSel$Destroy()
    }
  )
  gtkAddCallback(okBut,"clicked",
    function(obj)
    {
      fileEntry<-gtkFileSelectionGetFilename(fileSel)

      # need to determine the file extension, which will be the second
      # entry after splitting on "."
      fileSplitExt<-unlist(strsplit(fileEntry,"\\."))
      fileExt<-fileSplitExt[length(fileSplitExt)]

      fileSplitName<-unlist(strsplit(fileEntry,"\\/"))
      fileName<-fileSplitName[length(fileSplitName)]
      # need to remove the extension from fileName
      fileName<-unlist(strsplit(fileName,"\\."))[1]

      upFileExt<-toupper(fileExt)

      newData<-switch(upFileExt,
        "R"=source(file=fileEntry),
        "RDA"=load(file=fileEntry,envir=.GlobalEnv),
        "TAB"=read.table(file=fileEntry,header=TRUE),
        "CSV"=read.table(file=fileEntry,header=TRUE,sep=";"),
        "RDATA"=load(file=fileEntry,envir=.GlobalEnv),
        "TXT"=read.table(file=fileEntry,header=TRUE),
        showNotDF(fileName)
      )

      # now need to decide whether to load newData
      if (upFileExt == "R" || upFileExt == "RDA" || upFileExt == "TAB" ||
          upFileExt == "CSV" || upFileExt == "RDATA")
        loadDF(newData,upFileExt,fileName)

      fileSel$Destroy()

      # need to update the control window
      oldAction<-get("oldAction",controlEnv)
      if (oldAction != "")
      {
        assign("curAction",get("oldAction",controlEnv),controlEnv)
        updateControlWindow()
      }
    }
  )
}

############
# loadDF checks whether the file just loaded was
# a dataframe - if it was a dataframe or matrix, it is
# added to dfList in dataEnv
############
loadDF<-function(newData,fileExt,fileName)
{
  if (fileExt=="R")
    actualData<-newData$value
  if (fileExt=="RDA" || fileExt=="RDATA")
    actualData<-eval(as.name(newData))
  if (fileExt=="TAB" || fileExt=="TXT" || fileExt=="CSV")
    actualData<-newData

  if (is.data.frame(actualData) || is.matrix(actualData))
  {
    dMessage<-new("gAddDataMessage", data=actualData, dataName=fileName)
    handleMessage(dMessage)
  }
  else
    showNotDF(fileName)
}


###############
# packagesWithData returns all packages that have data sets
###############
packagesWithData<-function()
{
  testDt<-data()
  packageData<-sort(unique(testDt[[3]][,1]))
  return(packageData)
}

###############
# getDataSetsInPack returns data sets from packages along with
# their descriptions
#
# pack is the package from which you want to get the data sets
###############
getDataSetsInPack<-function(pack="all")
{
  if (pack=="all")
    testDt<-data()
  else
    testDt<-data(package=pack)

  dataSets<-testDt[[3]][,3]
  datOrder<-order(dataSets)
  # to alphabetize and order correctly (so the description order
  # matches the data set ordering)
  sortDataSets<-dataSets[datOrder]
  dataDesc<-testDt[[3]][datOrder,4]
  return(list(dataSets=sortDataSets,dataDesc=dataDesc))
}

##########
# getLoadedDF gets the names of the loaded dataframes in dataEnv
##########
getLoadedDF<-function()
{
  if (length(dfList) > 0)
    dfNames<-sort(names(dfList))
  else
    dfNames<-c()

  return(dfNames)
}
environment(getLoadedDF)<-dataEnv

###############
# createColorBrowser creates the color selection dialog so the user
# can pick a color
#
# note that the color browser is not modal so the user can choose
# whenever they would like to set the color to something new
###############
createColorBrowser<-function()
{
  w<-gtkColorSelectionDialog(title="Pick a Color",show=FALSE)
  assign("colorBrowser",w,controlEnv)

  # w gives the whole window - we want to get certain elements of the window
  okBut<-gtkColorSelectionDialogGetOkButton(w)
  canBut<-gtkColorSelectionDialogGetCancelButton(w)
  helpBut<-gtkColorSelectionDialogGetHelpButton(w)
  colorSel<-gtkColorSelectionDialogGetColorsel(w)
  # hide the Help button
  gtkWidgetHide(helpBut)
  w$Show()

  # this callback occurs whenever the user clicks on the color selection
  # object
  gtkAddCallback(colorSel,"color-changed",
    function(obj)
    {
      curColor<-gtkColorSelectionGetColor(colorSel)
#      print(curColor)
      curRed<-curColor[1]
      curGreen<-curColor[2]
      curBlue<-curColor[3]
      curColor<-rgb(curRed,curGreen,curBlue)
      assign("currentColor",curColor,controlEnv)
    }
  )

  # need to write callbacks for these buttons
  gtkAddCallback(okBut,"clicked",
    function(obj)
    {
      # need to set the color and destroy w
      curColor<-gtkColorSelectionGetColor(colorSel)
      curRed<-curColor[1]
      curGreen<-curColor[2]
      curBlue<-curColor[3]
      curColor<-rgb(curRed,curGreen,curBlue)
      # now assign the color to the environment
      assign("currentColor",curColor,controlEnv)
      assign("colorBrowser",c(),controlEnv)
      w$Destroy()
    }
  )
  gtkAddCallback(canBut,"clicked",
    function(obj)
    {
      assign("colorBrowser",c(),controlEnv)
      w$Destroy()
    }
  )

  gtkAddCallback(w,"delete_event",
    function(obj,ev)
    {
      assign("colorBrowser",c(),controlEnv)
      w$Destroy()
    }
  )
}

##########
# setIdentifyMode sets viewMode to identify
##########
setIdentifyMode<-function()
{
  setViewMode("identify")
}


##########
# setHighlightMode sets viewMode to highlight
##########
setHighlightMode<-function()
{
  setViewMode("highlight")
}

##########
# setHideMode sets viewMode to hide
##########
setHideMode<-function()
{
  setViewMode("hide")
}

##########
# setColorMode sets viewMode to color
# also need to create a color browser so the user can choose a color
##########
setColorMode<-function()
{
  setViewMode("color")
  cBrowser<-get("colorBrowser",controlEnv)
  if (length(cBrowser) == 0)
    createColorBrowser()
}

##########
# setViewMode sets the viewing mode in controlEnv and changes
# the title of all plots to reflect the view mode -
# may want to rethink having the plot title be the view mode???
##########
setViewMode<-function(mode)
{
  # setting the view mode means that the data will be modified
  assign("viewMode",mode,controlEnv)
  # now need to set the title of the plots
  plotList<-getPlotsFromViews()

  if (length(plotList) > 0)
  {
    # get all devices and then for each device set title
    allDevs<-unlist(lapply(plotList,function(x){plotDevice(x)}))

    origDev<-dev.cur()

    for (i in 1:length(allDevs))
    {
      dev.set(allDevs[i])

      # need to get the background color for each plot
      parbg<-plotPar(plotList[[i]])$bg

      #first remove old title
      if (parbg=="transparent")
        backCol<-"white"
      else
        backCol<-parbg

      title(get("plotTitle",controlEnv),col.main=backCol)

      if (mode != "")
      {
        title(mode,col.main=get("mainTitleColor",viewEnv))
        # don't reset the plot title until end of loop
      }
    }

    assign("plotTitle",mode,controlEnv)

    #reset the original device
    dev.set(origDev)
  }
}










## need to require some libraries
## need: RGtk, gtkDevice
.First.lib<-function(libname,pkgname)
{
  # need the first two libraries for Gtk widgets
    if (interactive()) {
        require(RGtk) || stop ("gtkIGraph requires package RGtk.")
        require(gtkDevice) || stop("gtkIGraph requires package gtkDevice.")
    }
    else {
        print("Can not use functionality w/o being interactive")
    }
}

