| # File src/library/grid/R/grob.R |
| # Part of the R package, https://www.R-project.org |
| # |
| # Copyright (C) 1995-2016 The R Core Team |
| # |
| # This program is free software; you can redistribute it and/or modify |
| # it under the terms of the GNU General Public License as published by |
| # the Free Software Foundation; either version 2 of the License, or |
| # (at your option) any later version. |
| # |
| # This program is distributed in the hope that it will be useful, |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| # GNU General Public License for more details. |
| # |
| # A copy of the GNU General Public License is available at |
| # https://www.R-project.org/Licenses/ |
| |
| ###################################### |
| # Grid graphical objects |
| ####################################### |
| |
| ################ |
| # CLASS DEFN |
| ################ |
| # A "virtual" class "gDesc" underlies both "grob" and "gPath" |
| |
| initGrobAutoName <- function() { |
| index <- 0 |
| function(prefix="GRID", suffix="GROB") { |
| index <<- index + 1 |
| paste(prefix, suffix, index, sep=".") |
| } |
| } |
| |
| grobAutoName <- initGrobAutoName() |
| |
| # Function for user to call to get "autogenerated" grob name |
| grobName <- function(grob=NULL, prefix="GRID") { |
| if (is.null(grob)) |
| grobAutoName(prefix) |
| else { |
| if (!is.grob(grob)) |
| stop("invalid 'grob' argument") |
| else |
| grobAutoName(prefix, class(grob)[1L]) |
| } |
| } |
| |
| ################ |
| # CLASS DEFN |
| ################ |
| # A grob has a name, a gp, and a vp |
| # grob inherits from gDesc |
| checkvpSlot <- function(vp) { |
| # vp can be a viewport, a viewport name, or a viewport path |
| if (!is.null(vp)) |
| if (!inherits(vp, "viewport") && |
| !inherits(vp, "vpPath") && |
| !is.character(vp)) |
| stop("invalid 'vp' slot") |
| # For interactive use, allow user to specify |
| # vpPath directly (i.e., w/o calling vpPath) |
| if (is.character(vp)) |
| vp <- vpPath(vp) |
| vp |
| } |
| |
| checkNameSlot <- function(x) { |
| # Supply a default name if one is not given |
| if (is.null(x$name)) |
| grobAutoName(suffix=class(x)[1L]) |
| else |
| as.character(x$name) |
| } |
| |
| checkgpSlot <- function(gp) { |
| # gp must be a gpar |
| if (!is.null(gp)) |
| if (!inherits(gp, "gpar")) |
| stop("invalid 'gp' slot") |
| } |
| |
| validDetails <- function(x) { |
| UseMethod("validDetails") |
| } |
| |
| validDetails.grob <- function(x) { |
| x |
| } |
| |
| validGrob <- function(x, ...) { |
| UseMethod("validGrob") |
| } |
| |
| validGrob.grob <- function(x, ...) { |
| # Validate class-specific slots |
| x <- validDetails(x) |
| # Validate standard grob slots |
| x$name <- checkNameSlot(x) |
| checkgpSlot(x$gp) |
| if (!is.null(x$vp)) |
| x$vp <- checkvpSlot(x$vp) |
| return(x) |
| } |
| |
| # This actually creates a new class derived from grob |
| # and returns an instance of that new class, all in one step |
| grob <- function(..., name=NULL, gp=NULL, vp=NULL, cl=NULL) { |
| g <- list(..., name=name, gp=gp, vp=vp) |
| if (!is.null(cl) && |
| !is.character(cl)) |
| stop("invalid 'grob' class") |
| class(g) <- c(cl, "grob", "gDesc") |
| validGrob(g) |
| } |
| |
| grid.grob <- function(list.struct, cl=NULL, draw=TRUE) .Defunct("grob") |
| |
| is.grob <- function(x) { |
| inherits(x, "grob") |
| } |
| |
| as.character.grob <- function(x, ...) { |
| paste0(class(x)[1L], "[", x$name, "]") |
| } |
| |
| print.grob <- function(x, ...) { |
| cat(as.character(x), "\n") |
| invisible(x) |
| } |
| |
| ################ |
| # gPath CLASS DEFN |
| ################ |
| # gPath is a concatenated list of names specifying a path to a grob |
| # Functions for creating "paths" of viewport names |
| |
| gPathFromVector <- function(names) { |
| if (any(bad <- !is.character(names))) |
| stop(ngettext(sum(bad), "invalid grob name", "invalid grob names"), |
| domain = NA) |
| # Break out any embedded .grid.pathSep's |
| names <- unlist(strsplit(names, .grid.pathSep)) |
| n <- length(names) |
| if (n < 1L) |
| stop("a 'grob' path must contain at least one 'grob' name") |
| path <- list(path = if (n==1) NULL else |
| paste(names[1L:(n-1)], collapse = .grid.pathSep), |
| name = names[n], n = n) |
| class(path) <- c("gPath", "path") |
| path |
| } |
| |
| gPath <- function(...) { |
| names <- c(...) |
| gPathFromVector(names) |
| } |
| |
| ################ |
| # gList CLASS DEFN |
| ################ |
| # Just a list of grobs |
| okGListelt <- function(x) { |
| is.grob(x) || is.null(x) || is.gList(x) |
| } |
| |
| is.gList <- function(x) { |
| inherits(x, "gList") |
| } |
| |
| as.gList <- function(x) { |
| if (is.null(x)) { |
| result <- list() |
| class(result) <- "gList" |
| } else if (is.grob(x)) { |
| result <- list(x) |
| class(result) <- "gList" |
| } else if (is.gList(x)) { |
| result <- x |
| } else { |
| stop("unable to coerce to \"gList\"") |
| } |
| result |
| } |
| |
| gList <- function(...) { |
| gl <- list(...) |
| if (length(gl) == 0L || |
| all(sapply(gl, okGListelt, simplify=TRUE))) { |
| # Ensure gList is "flat" |
| # Don't want gList containing gList ... |
| if (!all(sapply(gl, is.grob))) |
| gl <- do.call("c", lapply(gl, as.gList)) |
| class(gl) <- c("gList") |
| return(gl) |
| } else { |
| stop("only 'grobs' allowed in \"gList\"") |
| } |
| } |
| |
| addToGList <- function(x, gList) { |
| UseMethod("addToGList") |
| } |
| |
| addToGList.default <- function(x, gList) { |
| if (is.null(x)) |
| gList |
| else |
| stop("invalid element to add to \"gList\"") |
| } |
| |
| addToGList.grob <- function(x, gList) { |
| if (is.null(gList)) |
| gList(x) |
| else { |
| gList[[length(gList) + 1L]] <- x |
| return(gList) |
| } |
| } |
| |
| addToGList.gList <- function(x, gList) { |
| gl <- c(gList, x) |
| class(gl) <- "gList" |
| return(gl) |
| } |
| |
| as.character.gList <- function(x, ...) { |
| paste0("(", paste(lapply(x, as.character), collapse=", "), ")") |
| } |
| |
| print.gList <- function(x, ...) { |
| cat(as.character(x), "\n") |
| invisible(x) |
| } |
| |
| `[.gList` <- function(x, index, ...) { |
| cl <- class(x) |
| result <- "["(unclass(x), index, ...) |
| class(result) <- cl |
| result |
| } |
| |
| ################ |
| # gTree CLASS DEFN |
| ################ |
| # gTree extends grob |
| # A gTree has additional children slot |
| childName <- function(x) { |
| x$name |
| } |
| |
| setChildren <- function(x, children) { |
| if (!inherits(x, "gTree")) |
| stop("can only set 'children' for a \"gTree\"") |
| if (!is.null(children) && |
| !inherits(children, "gList")) |
| stop("'children' must be a \"gList\"") |
| # Thin out NULL children |
| if (!is.null(children)) { |
| cl <- class(children) |
| children <- children[!sapply(children, is.null)] |
| class(children) <- cl |
| } |
| if (length(children)) { |
| x$children <- children |
| childNames <- sapply(children, childName) |
| names(x$children) <- childNames |
| x$childrenOrder <- childNames |
| } else { |
| x$children <- gList() |
| x$childrenOrder <- character() |
| } |
| x |
| } |
| |
| childNames <- function(gTree) { |
| if (!inherits(gTree, "gTree")) |
| stop("it is only valid to get 'children' from a \"gTree\"") |
| gTree$childrenOrder |
| } |
| |
| validGrob.gTree <- function(x, childrenvp, ...) { |
| # Validate class-specific slots |
| x <- validDetails(x) |
| # Validate standard grob slots |
| x$name <- checkNameSlot(x) |
| checkgpSlot(x$gp) |
| if (!is.null(x$vp)) |
| x$vp <- checkvpSlot(x$vp) |
| # Only add childrenvp here so that gTree slots can |
| # be validated before childrenvp get made |
| # (making of childrenvp and children likely to depend |
| # on gTree slots) |
| if (!is.null(childrenvp)) |
| x$childrenvp <- checkvpSlot(childrenvp) |
| return(x) |
| } |
| |
| gTree <- function(..., name=NULL, gp=NULL, vp=NULL, |
| children=NULL, childrenvp=NULL, |
| cl=NULL) { |
| gt <- list(..., name=name, gp=gp, vp=vp) |
| if (!is.null(cl) && |
| !is.character(cl)) |
| stop("invalid \"gTree\" class") |
| class(gt) <- c(cl, "gTree", "grob", "gDesc") |
| gt <- validGrob(gt, childrenvp) |
| gt <- setChildren(gt, children) |
| return(gt) |
| } |
| |
| # A basic gTree that is JUST a collection of grobs |
| # (simply interface to gTree) |
| grobTree <- function(..., name=NULL, gp=NULL, vp=NULL, |
| childrenvp=NULL, cl=NULL) { |
| gTree(children=gList(...), |
| name=name, gp=gp, vp=vp, |
| childrenvp=childrenvp, cl=cl) |
| } |
| |
| ################ |
| # Getting just the names of the top-level grobs on the DL |
| ################ |
| getName <- function(elt) { |
| if (inherits(elt, "grob")) |
| elt$name |
| else |
| "" |
| } |
| |
| getNames <- function() { |
| dl <- grid.Call(C_getDisplayList)[1L:grid.Call(C_getDLindex)] |
| names <- sapply(dl, getName) |
| names[nzchar(names)] |
| } |
| |
| ################ |
| # Getting/adding/removing/editing (children of [children of ...]) a gTree |
| ################ |
| |
| # NOTE: In order to cut down on repeated code, some of these |
| # (i.e., all but get and set) are inefficient and call get/set |
| # to do their work. If speed becomes an issue, may have to |
| # revert to individual support for each function with highly |
| # repetitive code |
| |
| # Get a grob from the display list |
| grid.get <- function(gPath, strict=FALSE, grep=FALSE, global=FALSE, |
| allDevices=FALSE) { |
| if (allDevices) |
| stop("'allDevices' not yet implemented") |
| if (is.character(gPath)) |
| gPath <- gPath(gPath) |
| if (!inherits(gPath, "gPath")) |
| stop("invalid 'gPath'") |
| if (!is.logical(grep)) |
| stop("invalid 'grep' value") |
| grep <- rep(grep, length.out=depth(gPath)) |
| getDLfromGPath(gPath, strict, grep, global) |
| } |
| |
| # Just different defaults to grid.get for convenience |
| # Justified by usage patterns of Hadley Wickham |
| grid.gget <- function(..., grep=TRUE, global=TRUE) { |
| grid.get(..., grep=grep, global=global) |
| } |
| |
| # Get a child (of a child, of a child, ...) of a grob |
| getGrob <- function(gTree, gPath, strict=FALSE, |
| grep=FALSE, global=FALSE) { |
| if (!inherits(gTree, "gTree")) |
| stop("it is only valid to get a child from a \"gTree\"") |
| if (is.character(gPath)) |
| gPath <- gPath(gPath) |
| if (!inherits(gPath, "gPath")) |
| stop("invalid 'gPath'") |
| if (depth(gPath) == 1 && strict) { |
| gTree$children[[gPath$name]] |
| } else { |
| if (!is.logical(grep)) |
| stop("invalid 'grep' value") |
| grep <- rep(grep, length.out=depth(gPath)) |
| getGTree(gTree, NULL, gPath, strict, grep, global) |
| } |
| } |
| |
| # Set a grob on the display list |
| # nor is it valid to specify a global destination (i.e., no global arg) |
| grid.set <- function(gPath, newGrob, strict=FALSE, grep=FALSE, |
| redraw=TRUE) { |
| if (is.character(gPath)) |
| gPath <- gPath(gPath) |
| if (!inherits(gPath, "gPath")) |
| stop("invalid 'gPath'") |
| if (!is.logical(grep)) |
| stop("invalid 'grep' value") |
| grep <- rep(grep, length.out=depth(gPath)) |
| result <- setDLfromGPath(gPath, newGrob, strict, grep) |
| # result$index will be non-zero if matched the gPath |
| if (result$index) { |
| # Get the current DL index |
| dl.index <- grid.Call(C_getDLindex) |
| # Destructively modify the DL elt |
| grid.Call(C_setDLindex, as.integer(result$index)) |
| grid.Call(C_setDLelt, result$grob) |
| # Reset the DL index |
| grid.Call(C_setDLindex, as.integer(dl.index)) |
| if (redraw) |
| draw.all() |
| } else { |
| stop("'gPath' does not specify a valid child") |
| } |
| } |
| |
| # Set a grob |
| # nor is it valid to specify a global destination (i.e., no global arg) |
| setGrob <- function(gTree, gPath, newGrob, strict=FALSE, grep=FALSE) { |
| if (!inherits(gTree, "gTree")) |
| stop("it is only valid to set a child of a \"gTree\"") |
| if (!inherits(newGrob, "grob")) |
| stop("it is only valid to set a 'grob' as child of a \"gTree\"") |
| if (is.character(gPath)) |
| gPath <- gPath(gPath) |
| if (!inherits(gPath, "gPath")) |
| stop("invalid 'gPath'") |
| if (!is.logical(grep)) |
| stop("invalid 'grep' value") |
| grep <- rep(grep, length.out=depth(gPath)) |
| if (depth(gPath) == 1 && strict) { |
| # gPath must specify an existing child |
| if (old.pos <- nameMatch(gPath$name, gTree$childrenOrder, grep)) { |
| # newGrob name must match existing name |
| if (match(gTree$childrenOrder[old.pos], newGrob$name, nomatch=0L)) { |
| gTree$children[[newGrob$name]] <- newGrob |
| } else { |
| stop(gettextf("New 'grob' name (%s) does not match 'gPath' (%s)", |
| newGrob$name, gPath), domain = NA) |
| } |
| } else { |
| stop("'gPath' does not specify a valid child") |
| } |
| } else { |
| gTree <- setGTree(gTree, NULL, gPath, newGrob, strict, grep) |
| if (is.null(gTree)) |
| stop("'gPath' does not specify a valid child") |
| } |
| gTree |
| } |
| |
| # Add a grob to a grob on the display list |
| grid.add <- function(gPath, child, strict=FALSE, |
| grep=FALSE, global=FALSE, allDevices=FALSE, |
| redraw=TRUE) { |
| if (allDevices) |
| stop("'allDevices' not yet implemented") |
| if (is.character(gPath)) |
| gPath <- gPath(gPath) |
| if (!inherits(gPath, "gPath")) |
| stop("invalid 'gPath'") |
| if (!is.logical(grep)) |
| stop("invalid 'grep' value") |
| grep <- rep(grep, length.out=depth(gPath)) |
| addDLfromGPath(gPath, child, strict, grep, global, redraw) |
| } |
| |
| # Add a grob to a gTree (or a child of a (child of a ...) gTree) |
| addGrob <- function(gTree, child, gPath=NULL, strict=FALSE, |
| grep=FALSE, global=FALSE, warn=TRUE) { |
| if (!inherits(child, "grob")) |
| stop("it is only valid to add a 'grob' to a \"gTree\"") |
| if (is.null(gPath)) { |
| addToGTree(gTree, child) |
| } else { |
| if (is.character(gPath)) |
| gPath <- gPath(gPath) |
| # Only makes sense to specify a gPath for a gTree |
| if (!inherits(gTree, "gTree")) |
| stop("it is only valid to add a child to a \"gTree\"") |
| if (!is.logical(grep)) |
| stop("invalid 'grep' value") |
| grep <- rep(grep, length.out=depth(gPath)) |
| # result will be NULL if no match |
| result <- addGTree(gTree, child, NULL, gPath, strict, grep, global) |
| if (is.null(result)) { |
| if (warn) |
| warning(gettextf("'gPath' (%s) not found", |
| as.character(gPath)), |
| domain = NA) |
| gTree |
| } else { |
| result |
| } |
| } |
| } |
| |
| # Remove a grob (or child of ...) from the display list |
| grid.remove <- function(gPath, warn=TRUE, strict=FALSE, |
| grep=FALSE, global=FALSE, allDevices=FALSE, |
| redraw=TRUE) { |
| if (allDevices) |
| stop("'allDevices' not yet implemented") |
| if (is.character(gPath)) |
| gPath <- gPath(gPath) |
| if (!inherits(gPath, "gPath")) |
| stop("invalid 'gPath'") |
| if (!is.logical(grep)) |
| stop("invalid 'grep' value") |
| grep <- rep(grep, length.out=depth(gPath)) |
| if (depth(gPath) == 1) { |
| removeNameFromDL(gPath$name, strict, grep, global, warn, redraw) |
| } else { |
| name <- gPath$name |
| gPath <- gPath(gPath$path) |
| greppath <- grep[-length(grep)] |
| grepname <- grep[length(grep)] |
| removeDLFromGPath(gPath, name, strict, greppath, grepname, |
| global, warn, redraw) |
| } |
| } |
| |
| # Just different defaults to grid.remove for convenience |
| # Justified by usage patterns of Hadley Wickham |
| grid.gremove <- function(..., grep=TRUE, global=TRUE) { |
| grid.remove(..., grep=grep, global=global) |
| } |
| |
| # Remove a child from a (child of ...) gTree |
| removeGrob <- function(gTree, gPath, strict=FALSE, |
| grep=FALSE, global=FALSE, warn=TRUE) { |
| if (!inherits(gTree, "gTree")) |
| stop("it is only valid to remove a child from a \"gTree\"") |
| if (is.character(gPath)) |
| gPath <- gPath(gPath) |
| if (!inherits(gPath, "gPath")) |
| stop("invalid 'gPath'") |
| if (!is.logical(grep)) |
| stop("invalid 'grep' value") |
| grep <- rep(grep, length.out=depth(gPath)) |
| if (depth(gPath) == 1) { |
| # result will be NULL if no match |
| result <- removeName(gTree, gPath$name, strict, grep, global, warn) |
| } else { |
| name <- gPath$name |
| gPath <- gPath(gPath$path) |
| greppath <- grep[-length(grep)] |
| grepname <- grep[length(grep)] |
| # result will be NULL if no match |
| result <- removeGTree(gTree, name, NULL, gPath, strict, |
| greppath, grepname, global, warn) |
| } |
| if (is.null(result)) { |
| if (warn) |
| warning(gettextf("'gPath' (%s) not found", as.character(gPath)), |
| domain = NA) |
| gTree |
| } else { |
| result |
| } |
| } |
| |
| # Edit a grob on the display list |
| grid.edit <- function(gPath, ..., strict=FALSE, |
| grep=FALSE, global=FALSE, allDevices=FALSE, |
| redraw=TRUE) { |
| if (allDevices) |
| stop("'allDevices' not yet implemented") |
| if (is.character(gPath)) |
| gPath <- gPath(gPath) |
| if (!inherits(gPath, "gPath")) |
| stop("invalid 'gPath'") |
| if (!is.logical(grep)) |
| stop("invalid 'grep' value") |
| grep <- rep(grep, length.out=depth(gPath)) |
| specs <- list(...) |
| editDLfromGPath(gPath, specs, strict, grep, global, redraw) |
| } |
| |
| # Just different defaults to grid.edit for convenience |
| # Justified by usage patterns of Hadley Wickham |
| grid.gedit <- function(..., grep=TRUE, global=TRUE) { |
| grid.edit(..., grep=grep, global=global) |
| } |
| |
| # Edit a (child of a ...) grob |
| editGrob <- function(grob, gPath=NULL, ..., strict=FALSE, |
| grep=FALSE, global=FALSE, warn=TRUE) { |
| specs <- list(...) |
| if (is.null(gPath)) { |
| editThisGrob(grob, specs) |
| } else { |
| if (is.character(gPath)) |
| gPath <- gPath(gPath) |
| # Only makes sense to specify a gPath for a gTree |
| if (!inherits(grob, "gTree")) |
| stop("it is only valid to edit a child of a \"gTree\"") |
| if (!is.logical(grep)) |
| stop("invalid 'grep' value") |
| grep <- rep(grep, length.out=depth(gPath)) |
| # result will be NULL if no match |
| result <- editGTree(grob, specs, NULL, gPath, strict, grep, global) |
| if (is.null(result)) { |
| if (warn) |
| warning(gettextf("'gPath' (%s) not found", |
| as.character(gPath)), |
| domain = NA) |
| grob |
| } else { |
| result |
| } |
| } |
| } |
| |
| ######### |
| # Generic "hook" to allow customised action on edit |
| ######### |
| editDetails <- function(x, specs) { |
| UseMethod("editDetails") |
| } |
| |
| editDetails.default <- function(x, specs) { |
| # Do nothing BUT return object being edited |
| x |
| } |
| |
| editDetails.gTree <- function(x, specs) { |
| # Disallow editing children or childrenOrder slots directly |
| if (any(specs %in% c("children", "childrenOrder"))) |
| stop("it is invalid to directly edit the 'children' or 'childrenOrder' slot") |
| x |
| } |
| |
| ######### |
| # Helper functions for getting/adding/removing/editing grobs |
| # |
| # ASSUME down here that the grep argument has been replicated |
| # up to the length of the gPath argument |
| ######### |
| |
| # Find a "match" between a path$name and a grob$name |
| nameMatch <- function(pathName, grobName, grep) { |
| if (grep) { |
| pos <- grep(pathName, grobName) |
| (length(pos) && pos == 1) |
| } else { |
| match(pathName, grobName, nomatch=0L) |
| } |
| } |
| |
| # Return the position of path$name in vector of names |
| # Return FALSE if not found |
| # If grep=TRUE, the answer may be a vector! |
| namePos <- function(pathName, names, grep) { |
| if (grep) { |
| pos <- grep(pathName, names) |
| if (length(pos) == 0L) |
| pos <- FALSE |
| } else { |
| pos <- match(pathName, names, nomatch=0L) |
| } |
| pos |
| } |
| |
| partialPathMatch <- function(pathsofar, path, strict=FALSE, grep) { |
| if (strict) { |
| if (!any(grep)) |
| length(grep(paste0("^", pathsofar), path)) > 0L |
| else { |
| pathSoFarElts <- explode(pathsofar) |
| pathElts <- explode(path) |
| ok <- TRUE |
| npsfe <- length(pathSoFarElts) |
| index <- 1 |
| while (ok & index <= npsfe) { |
| if (grep[index]) |
| ok <- (grep(pathSoFarElts[index], pathElts[index]) == 1) |
| else |
| ok <- match(pathSoFarElts[index], pathElts[index], nomatch=0L) |
| index <- index + 1 |
| } |
| ok |
| } |
| } else { |
| # If we're not doing strict matching then anything from a full |
| # path match to absolutely no match means a partial match |
| # (i.e., keep looking) |
| TRUE |
| } |
| } |
| |
| fullPathMatch <- function(pathsofar, gPath, strict, grep) { |
| if (is.null(pathsofar)) |
| match <- (depth(gPath) == 1) |
| else { |
| path <- gPath$path |
| if (!any(grep)) |
| if (strict) |
| match <- match(pathsofar, path, nomatch=0L) |
| else |
| match <- (length(grep(paste0(path, "$"), pathsofar)) > 0L) |
| else { |
| pathSoFarElts <- explode(pathsofar) |
| pathElts <- explode(path) |
| npsfe <- length(pathSoFarElts) |
| npe <- length(pathElts) |
| if (npe > npsfe) { |
| match <- FALSE |
| } else { |
| match <- TRUE |
| index <- 1 |
| if (strict) {# pathSoFar same length as gPath |
| } else {# pathSoFar could be longer than gPath |
| pathSoFarElts <- pathSoFarElts[(npsfe - npe + 1):npsfe] |
| } |
| while (match && index <= npe) { |
| if (grep[index]) |
| match <- (length(grep(pathElts[index], pathSoFarElts[index])) > 0L) |
| else |
| match <- match(pathSoFarElts[index], pathElts[index], nomatch = 0L) |
| index <- index + 1 |
| } |
| } |
| } |
| } |
| match |
| } |
| |
| ##### |
| ##### Get support |
| ##### |
| |
| # Add a grob to a result |
| growResult <- function(result, x) { |
| UseMethod("growResult") |
| } |
| |
| # Should only be when result is NULL |
| growResult.default <- function(result, x) { |
| if (!is.null(result)) |
| stop("invalid 'result'") |
| x |
| } |
| |
| growResult.grob <- function(result, x) { |
| if (is.grob(x)) |
| gList(result, x) |
| else |
| # x should be a gList |
| addToGList(result, x) |
| } |
| |
| growResult.gList <- function(result, x) { |
| addToGList(x, result) |
| } |
| |
| # A gPath may specify the child of a gTree |
| # (or the child of a child of a gTree, or ...) |
| getGrobFromGPath <- function(grob, pathsofar, gPath, strict, |
| grep, global) { |
| UseMethod("getGrobFromGPath") |
| } |
| |
| # If it's not a grob then fail |
| # Handles case when traversing DL |
| getGrobFromGPath.default <- function(grob, pathsofar, gPath, strict, |
| grep, global) { |
| NULL |
| } |
| |
| getGrobFromGPath.grob <- function(grob, pathsofar, gPath, strict, |
| grep, global) { |
| if (depth(gPath) > 1) |
| NULL |
| else { |
| if (nameMatch(gPath$name, grob$name, grep)) |
| grob |
| else |
| NULL |
| } |
| } |
| |
| getGTree <- function(gTree, pathsofar, gPath, strict, grep, global) { |
| # Try to find pathsofar at start of gPath |
| # NOTE: may be called directly with pathsofar=NULL |
| if (is.null(pathsofar) || |
| (!strict && depth(gPath) == 1) || |
| partialPathMatch(pathsofar, gPath$path, strict, grep)) { |
| found <- FALSE |
| index <- 1 |
| grob <- NULL |
| # Search children for match |
| while (index <= length(gTree$childrenOrder) && |
| (!found || global)) { |
| childName <- gTree$childrenOrder[index] |
| child <- gTree$children[[childName]] |
| # Special case when strict is FALSE and depth(gPath) is 1 |
| # Just check for gPath$name amongst children and recurse if no match |
| if (!strict && depth(gPath) == 1) { |
| if (nameMatch(gPath$name, childName, grep)) { |
| grob <- growResult(grob, child) |
| found <- TRUE |
| } else { |
| if (is.null(pathsofar)) |
| newpathsofar <- child$name |
| else |
| newpathsofar <- paste0(pathsofar, .grid.pathSep, childName) |
| if (!is.null(newChild <- getGrobFromGPath(child, newpathsofar, |
| gPath, strict, |
| grep, global))) { |
| grob <- growResult(grob, newChild) |
| found <- TRUE |
| } |
| } |
| } else { |
| # Only check for match with child if have full match with pathsofar |
| # If it's a complete match, look for gPath$name amongst child |
| # NOTE: may be called directly with pathsofar=NULL |
| if (fullPathMatch(pathsofar, gPath, strict, grep)) { |
| if (nameMatch(gPath$name, childName, grep[depth(gPath)])) { |
| grob <- growResult(grob, child) |
| found <- TRUE |
| } |
| # Otherwise recurse down child |
| } else { |
| # NOTE: may be called directly with pathsofar=NULL |
| if (is.null(pathsofar)) |
| newpathsofar <- child$name |
| else |
| newpathsofar <- paste0(pathsofar, .grid.pathSep, childName) |
| if (!is.null(newChild <- getGrobFromGPath(child, newpathsofar, |
| gPath, strict, |
| grep, global))) { |
| grob <- growResult(grob, newChild) |
| found <- TRUE |
| } |
| } |
| } |
| index <- index + 1 |
| } |
| if (found) |
| grob |
| else |
| NULL |
| } else { |
| NULL |
| } |
| } |
| |
| getGrobFromGPath.gTree <- function(grob, pathsofar, gPath, strict, |
| grep, global) { |
| if (depth(gPath) == 1) { |
| if (nameMatch(gPath$name, grob$name, grep)) |
| grob |
| else |
| if (strict) |
| NULL |
| else |
| getGTree(grob, |
| if (is.null(pathsofar)) grob$name else pathsofar, |
| gPath, strict, grep, global) |
| } else { |
| getGTree(grob, |
| if (is.null(pathsofar)) grob$name else pathsofar, |
| gPath, strict, grep, global) |
| } |
| } |
| |
| getDLfromGPath <- function(gPath, strict, grep, global) { |
| dl.index <- grid.Call(C_getDLindex) |
| result <- NULL |
| index <- 1 |
| while (index < dl.index && |
| (is.null(result) || global)) { |
| grob <- getGrobFromGPath(grid.Call(C_getDLelt, |
| as.integer(index)), |
| NULL, gPath, strict, |
| grep, global) |
| if (!is.null(grob)) |
| result <- growResult(result, grob) |
| index <- index + 1 |
| } |
| result |
| } |
| |
| ##### |
| ##### Set support |
| ##### |
| # A gPath may specify the child of a gTree |
| # (or the child of a child of a gTree, or ...) |
| setGrobFromGPath <- function(grob, pathsofar, gPath, newGrob, strict, grep) { |
| UseMethod("setGrobFromGPath") |
| } |
| |
| # Ignore DL elements which are not grobs |
| setGrobFromGPath.default <- function(grob, pathsofar, gPath, newGrob, |
| strict, grep) { |
| NULL |
| } |
| |
| setGrobFromGPath.grob <- function(grob, pathsofar, gPath, newGrob, |
| strict, grep) { |
| if (depth(gPath) > 1) |
| NULL |
| else { |
| if (nameMatch(gPath$name, grob$name, grep)) |
| if (match(grob$name, newGrob$name, nomatch=0L)) |
| newGrob |
| else |
| NULL |
| else |
| NULL |
| } |
| } |
| |
| # Try to match gPath in gTree children |
| # Return NULL if cant' find match |
| # Return modified gTree if can find match |
| setGTree <- function(gTree, pathsofar, gPath, newGrob, strict, grep) { |
| # Try to find pathsofar at start of gPath |
| # NOTE: may be called directly with pathsofar=NULL |
| if (is.null(pathsofar) || |
| (!strict && depth(gPath) == 1) || |
| partialPathMatch(pathsofar, gPath$path, strict, grep)) { |
| found <- FALSE |
| index <- 1 |
| # Search children for match |
| while (index <= length(gTree$childrenOrder) && !found) { |
| childName <- gTree$childrenOrder[index] |
| child <- gTree$children[[childName]] |
| # Special case when strict is FALSE and depth(gPath) is 1 |
| # Just check for gPath$name amongst children and recurse if no match |
| if (!strict && depth(gPath) == 1) { |
| if (nameMatch(gPath$name, childName, grep)) { |
| if (match(childName, newGrob$name, nomatch=0L)) { |
| gTree$children[[newGrob$name]] <- newGrob |
| found <- TRUE |
| } else { |
| stop("the new 'grob' must have the same name as the old 'grob'") |
| } |
| } else { |
| if (is.null(pathsofar)) |
| newpathsofar <- child$name |
| else |
| newpathsofar <- paste0(pathsofar, .grid.pathSep, childName) |
| if (!is.null(newChild <- setGrobFromGPath(child, newpathsofar, |
| gPath, newGrob, |
| strict, grep))) { |
| gTree$children[[childName]] <- newChild |
| found <- TRUE |
| } |
| } |
| } else { |
| # Only check for match with child if have full match with pathsofar |
| # If it's a complete match, look for gPath$name amongst child |
| # NOTE: may be called directly with pathsofar=NULL |
| if (fullPathMatch(pathsofar, gPath, strict, grep)) { |
| if (nameMatch(gPath$name, childName, grep[depth(gPath)])) { |
| if (match(childName, newGrob$name, nomatch=0L)) { |
| gTree$children[[newGrob$name]] <- newGrob |
| found <- TRUE |
| } else { |
| stop("the new 'grob' must have the same name as the old 'grob'") |
| } |
| } |
| # Otherwise recurse down child |
| } else { |
| # NOTE: may be called directly with pathsofar=NULL |
| if (is.null(pathsofar)) |
| newpathsofar <- child$name |
| else |
| newpathsofar <- paste0(pathsofar, .grid.pathSep, childName) |
| if (!is.null(newChild <- setGrobFromGPath(child, newpathsofar, |
| gPath, newGrob, |
| strict, grep))) { |
| gTree$children[[childName]] <- newChild |
| found <- TRUE |
| } |
| } |
| } |
| index <- index + 1 |
| } |
| if (found) |
| gTree |
| else |
| NULL |
| } else { |
| NULL |
| } |
| } |
| |
| setGrobFromGPath.gTree <- function(grob, pathsofar, gPath, newGrob, |
| strict, grep) { |
| if (depth(gPath) == 1) { |
| if (nameMatch(gPath$name, grob$name, grep)) |
| if (match(grob$name, newGrob$name, nomatch=0L)) |
| newGrob |
| else |
| stop("the new 'grob' must have the same name as the old 'grob'") |
| else |
| if (strict) |
| NULL |
| else |
| setGTree(grob, |
| if (is.null(pathsofar)) grob$name else pathsofar, |
| gPath, newGrob, strict, grep) |
| } else { |
| setGTree(grob, |
| # Initialise pathsofar if first time through |
| if (is.null(pathsofar)) grob$name else pathsofar, |
| gPath, newGrob, strict, grep) |
| } |
| } |
| |
| setDLfromGPath <- function(gPath, newGrob, strict, grep) { |
| dl.index <- grid.Call(C_getDLindex) |
| index <- 1 |
| result <- list(index=0, grob=NULL) |
| while (index < dl.index && |
| result$index == 0) { |
| result$grob <- setGrobFromGPath(grid.Call(C_getDLelt, |
| as.integer(index)), |
| NULL, gPath, newGrob, strict, grep) |
| if (!is.null(result$grob)) |
| result$index <- index |
| index <- index + 1 |
| } |
| result |
| } |
| |
| ##### |
| ##### Edit support |
| ##### |
| editThisGrob <- function(grob, specs) { |
| for (i in names(specs)) |
| if (nzchar(i)) |
| # Handle gp as special case |
| if (match(i, "gp", nomatch=0)) |
| # Handle NULL as special case |
| if (is.null(specs[[i]])) |
| grob[i] <- list(gp=NULL) |
| else |
| grob$gp <- mod.gpar(grob$gp, specs$gp) |
| # If there is no slot with the argument name, just ignore that argument |
| else if (match(i, names(grob), nomatch=0)) |
| # Handle NULL as special case |
| if (is.null(specs[[i]])) |
| grob[i] <- eval(substitute(list(i=NULL))) |
| else |
| grob[[i]] <- specs[[i]] |
| else |
| warning(gettextf("slot '%s' not found", i), domain = NA) |
| # Check grob slots are ok before trying to do anything with them |
| # in editDetails |
| # grob$childrenvp may be non-NULL for a gTree |
| grob <- validGrob(grob, grob$childrenvp) |
| editDetails(grob, specs) |
| } |
| |
| # A gPath may specify the child of a gTree |
| # (or the child of a child of a gTree, or ...) |
| editGrobFromGPath <- function(grob, specs, pathsofar, gPath, strict, |
| grep, global) { |
| UseMethod("editGrobFromGPath") |
| } |
| |
| # If it's not a grob then fail |
| # Handles case when traversing DL |
| editGrobFromGPath.default <- function(grob, specs, |
| pathsofar, gPath, strict, |
| grep, global) { |
| NULL |
| } |
| |
| editGrobFromGPath.grob <- function(grob, specs, |
| pathsofar, gPath, strict, |
| grep, global) { |
| if (depth(gPath) > 1) |
| NULL |
| else { |
| if (nameMatch(gPath$name, grob$name, grep)) |
| editThisGrob(grob, specs) |
| else |
| NULL |
| } |
| } |
| |
| editGTree <- function(gTree, specs, pathsofar, gPath, strict, |
| grep, global) { |
| # Try to find pathsofar at start of gPath |
| # NOTE: may be called directly with pathsofar=NULL |
| if (is.null(pathsofar) || |
| (!strict && depth(gPath) == 1) || |
| partialPathMatch(pathsofar, gPath$path, strict, grep)) { |
| found <- FALSE |
| index <- 1 |
| # Search children for match |
| while (index <= length(gTree$childrenOrder) && |
| (!found || global)) { |
| childName <- gTree$childrenOrder[index] |
| child <- gTree$children[[childName]] |
| # Special case when strict is FALSE and depth(gPath) is 1 |
| # Just check for gPath$name amongst children and recurse if no match |
| if (!strict && depth(gPath) == 1) { |
| if (nameMatch(gPath$name, childName, grep)) { |
| gTree$children[[childName]] <- editThisGrob(child, specs) |
| found <- TRUE |
| } else { |
| if (is.null(pathsofar)) |
| newpathsofar <- child$name |
| else |
| newpathsofar <- paste0(pathsofar, .grid.pathSep, childName) |
| if (!is.null(newChild <- editGrobFromGPath(child, specs, |
| newpathsofar, |
| gPath, strict, |
| grep, global))) { |
| gTree$children[[childName]] <- newChild |
| found <- TRUE |
| } |
| } |
| } else { |
| # Only check for match with child if have full match with pathsofar |
| # If it's a complete match, look for gPath$name amongst child |
| # NOTE: may be called directly with pathsofar=NULL |
| if (fullPathMatch(pathsofar, gPath, strict, grep)) { |
| if (nameMatch(gPath$name, childName, grep[depth(gPath)])) { |
| gTree$children[[childName]] <- editThisGrob(child, specs) |
| found <- TRUE |
| } |
| # Otherwise recurse down child |
| } else { |
| # NOTE: may be called directly with pathsofar=NULL |
| if (is.null(pathsofar)) |
| newpathsofar <- child$name |
| else |
| newpathsofar <- paste0(pathsofar, .grid.pathSep, childName) |
| if (!is.null(newChild <- editGrobFromGPath(child, specs, |
| newpathsofar, |
| gPath, strict, |
| grep, global))) { |
| gTree$children[[childName]] <- newChild |
| found <- TRUE |
| } |
| } |
| } |
| index <- index + 1 |
| } |
| if (found) |
| gTree |
| else |
| NULL |
| } else { |
| NULL |
| } |
| } |
| |
| editGrobFromGPath.gTree <- function(grob, specs, |
| pathsofar, gPath, strict, |
| grep, global) { |
| if (depth(gPath) == 1) { |
| if (nameMatch(gPath$name, grob$name, grep)) |
| editThisGrob(grob, specs) |
| else |
| if (strict) |
| NULL |
| else |
| editGTree(grob, specs, |
| if (is.null(pathsofar)) grob$name else pathsofar, |
| gPath, strict, grep, global) |
| } else { |
| editGTree(grob, specs, |
| if (is.null(pathsofar)) grob$name else pathsofar, |
| gPath, strict, grep, global) |
| } |
| } |
| |
| editDLfromGPath <- function(gPath, specs, strict, grep, global, redraw) { |
| dl.index <- grid.Call(C_getDLindex) |
| index <- 1 |
| grob <- NULL |
| found <- FALSE |
| while (index < dl.index && |
| (is.null(grob) || global)) { |
| grob <- editGrobFromGPath(grid.Call(C_getDLelt, |
| as.integer(index)), |
| specs, |
| NULL, gPath, strict, grep, global) |
| if (!is.null(grob)) { |
| # Destructively modify the DL elt |
| grid.Call(C_setDLindex, as.integer(index)) |
| grid.Call(C_setDLelt, grob) |
| # Reset the DL index |
| grid.Call(C_setDLindex, as.integer(dl.index)) |
| found <- TRUE |
| } |
| index <- index + 1 |
| } |
| if (!found) |
| stop(gettextf("'gPath' (%s) not found", as.character(gPath)), domain = NA) |
| else if (redraw) |
| draw.all() |
| } |
| |
| ##### |
| ##### Add support |
| ##### |
| |
| # Assume that child is a grob |
| addToGTree <- function(gTree, child) { |
| if (!inherits(gTree, "gTree")) |
| stop("it is only valid to add a child to a \"gTree\"") |
| gTree$children[[child$name]] <- child |
| # Handle case where child name already exists (so will be overwritten) |
| if (old.pos <- match(child$name, gTree$childrenOrder, nomatch=0)) |
| gTree$childrenOrder <- gTree$childrenOrder[-old.pos] |
| gTree$childrenOrder <- c(gTree$childrenOrder, child$name) |
| gTree |
| } |
| |
| # A gPath may specify the child of a gTree |
| # (or the child of a child of a gTree, or ...) |
| addGrobFromGPath <- function(grob, child, pathsofar, gPath, strict, |
| grep, global) { |
| UseMethod("addGrobFromGPath") |
| } |
| |
| # If it's not a grob then fail |
| # Handles case when traversing DL |
| addGrobFromGPath.default <- function(grob, child, |
| pathsofar, gPath, strict, |
| grep, global) { |
| NULL |
| } |
| |
| # If no match then fail |
| # If match then error! |
| addGrobFromGPath.grob <- function(grob, child, |
| pathsofar, gPath, strict, |
| grep, global) { |
| if (depth(gPath) > 1) |
| NULL |
| else { |
| if (nameMatch(gPath$name, grob$name, grep)) |
| stop("it is only valid to add a child to a \"gTree\"") |
| else |
| NULL |
| } |
| } |
| |
| # In this function, the grob being added is called "grob" |
| # (in all others it is called "child" |
| addGTree <- function(gTree, grob, pathsofar, gPath, strict, |
| grep, global) { |
| # Try to find pathsofar at start of gPath |
| # NOTE: may be called directly with pathsofar=NULL |
| if (is.null(pathsofar) || |
| (!strict && depth(gPath) == 1) || |
| partialPathMatch(pathsofar, gPath$path, strict, grep)) { |
| found <- FALSE |
| index <- 1 |
| # Search children for match |
| while (index <= length(gTree$childrenOrder) && |
| (!found || global)) { |
| childName <- gTree$childrenOrder[index] |
| child <- gTree$children[[childName]] |
| # Special case when strict is FALSE and depth(gPath) is 1 |
| # Just check for gPath$name amongst children and recurse if no match |
| if (!strict && depth(gPath) == 1) { |
| if (nameMatch(gPath$name, childName, grep)) { |
| gTree$children[[childName]] <- addToGTree(child, grob) |
| found <- TRUE |
| } else { |
| if (is.null(pathsofar)) |
| newpathsofar <- child$name |
| else |
| newpathsofar <- paste0(pathsofar, .grid.pathSep, childName) |
| if (!is.null(newChild <- addGrobFromGPath(child, grob, |
| newpathsofar, |
| gPath, strict, |
| grep, global))) { |
| gTree$children[[childName]] <- newChild |
| found <- TRUE |
| } |
| } |
| } else { |
| # Only check for match with child if have full match with pathsofar |
| # If it's a complete match, look for gPath$name amongst child |
| # NOTE: may be called directly with pathsofar=NULL |
| if (fullPathMatch(pathsofar, gPath, strict, grep)) { |
| if (nameMatch(gPath$name, childName, grep[depth(gPath)])) { |
| gTree$children[[childName]] <- addToGTree(child, grob) |
| found <- TRUE |
| } |
| # Otherwise recurse down child |
| } else { |
| # NOTE: may be called directly with pathsofar=NULL |
| if (is.null(pathsofar)) |
| newpathsofar <- child$name |
| else |
| newpathsofar <- paste0(pathsofar, .grid.pathSep, childName) |
| if (!is.null(newChild <- addGrobFromGPath(child, grob, |
| newpathsofar, |
| gPath, strict, |
| grep, global))) { |
| gTree$children[[childName]] <- newChild |
| found <- TRUE |
| } |
| } |
| } |
| index <- index + 1 |
| } |
| if (found) |
| gTree |
| else |
| NULL |
| } else { |
| NULL |
| } |
| } |
| |
| addGrobFromGPath.gTree <- function(grob, child, |
| pathsofar, gPath, strict, |
| grep, global) { |
| if (depth(gPath) == 1) { |
| if (nameMatch(gPath$name, grob$name, grep)) |
| addToGTree(grob, child) |
| else |
| if (strict) |
| NULL |
| else |
| addGTree(grob, child, |
| if (is.null(pathsofar)) grob$name else pathsofar, |
| gPath, strict, grep, global) |
| } else { |
| addGTree(grob, child, |
| if (is.null(pathsofar)) grob$name else pathsofar, |
| gPath, strict, grep, global) |
| } |
| } |
| |
| addDLfromGPath <- function(gPath, child, strict, grep, global, redraw) { |
| dl.index <- grid.Call(C_getDLindex) |
| index <- 1 |
| grob <- NULL |
| found <- FALSE |
| while (index < dl.index && |
| (is.null(grob) || global)) { |
| grob <- addGrobFromGPath(grid.Call(C_getDLelt, |
| as.integer(index)), |
| child, |
| NULL, gPath, strict, grep, global) |
| if (!is.null(grob)) { |
| # Destructively modify the DL elt |
| grid.Call(C_setDLindex, as.integer(index)) |
| grid.Call(C_setDLelt, grob) |
| # Reset the DL index |
| grid.Call(C_setDLindex, as.integer(dl.index)) |
| found <- TRUE |
| } |
| index <- index + 1 |
| } |
| if (!found) |
| stop(gettextf("'gPath' (%s) not found", gPath), domain = NA) |
| else if (redraw) |
| draw.all() |
| } |
| |
| ##### |
| ##### Remove support |
| ##### |
| |
| removeFromGTree <- function(gTree, name, grep) { |
| if (!inherits(gTree, "gTree")) |
| stop("it is only valid to remove a child from a \"gTree\"") |
| if (grep) { |
| old.pos <- grep(name, gTree$childrenOrder) |
| if (length(old.pos) == 0L) |
| old.pos <- 0 |
| } else { |
| old.pos <- match(name, gTree$childrenOrder, nomatch=0) |
| } |
| if (old.pos > 0) { |
| # name might be a regexp so use real name |
| gTree$children[[gTree$childrenOrder[old.pos]]] <- NULL |
| gTree$childrenOrder <- gTree$childrenOrder[-old.pos] |
| gTree |
| } else { |
| NULL |
| } |
| } |
| |
| # A gPath may specify the child of a gTree |
| # (or the child of a child of a gTree, or ...) |
| removeGrobFromGPath <- function(grob, name, pathsofar, gPath, strict, |
| grep, grepname, global, warn) { |
| UseMethod("removeGrobFromGPath") |
| } |
| |
| # If it's not a grob then fail |
| # Handles case when traversing DL |
| removeGrobFromGPath.default <- function(grob, name, |
| pathsofar, gPath, strict, |
| grep, grepname, global, warn) { |
| NULL |
| } |
| |
| # ALWAYS fail |
| # (either no match or match but grob has no children!) |
| removeGrobFromGPath.grob <- function(grob, name, |
| pathsofar, gPath, strict, |
| grep, grepname, global, warn) { |
| NULL |
| } |
| |
| removeGTree <- function(gTree, name, pathsofar, gPath, strict, |
| grep, grepname, global, warn) { |
| # Try to find pathsofar at start of gPath |
| # NOTE: may be called directly with pathsofar=NULL |
| if (is.null(pathsofar) || |
| (!strict && depth(gPath) == 1) || |
| partialPathMatch(pathsofar, gPath$path, strict, grep)) { |
| found <- FALSE |
| index <- 1 |
| # Search children for match |
| while (index <= length(gTree$childrenOrder) && |
| (!found || global)) { |
| childName <- gTree$childrenOrder[index] |
| child <- gTree$children[[childName]] |
| # Special case when strict is FALSE and depth(gPath) is 1 |
| # Just check for gPath$name amongst children and recurse if no match |
| if (!strict && depth(gPath) == 1) { |
| # NOTE: child has to be a gTree if we hope to find a child in it! |
| if (inherits(child, "gTree") && |
| nameMatch(gPath$name, childName, grep)) { |
| newchild <- removeFromGTree(child, name, grepname) |
| if (!is.null(newchild)) { |
| gTree$children[[childName]] <- newchild |
| found <- TRUE |
| } |
| } else { |
| if (is.null(pathsofar)) |
| newpathsofar <- child$name |
| else |
| newpathsofar <- paste0(pathsofar, .grid.pathSep, childName) |
| if (!is.null(newChild <- removeGrobFromGPath(child, name, |
| newpathsofar, |
| gPath, strict, |
| grep, grepname, |
| global, warn))) { |
| gTree$children[[childName]] <- newChild |
| found <- TRUE |
| } |
| } |
| } else { |
| # Only check for match with child if have full match with pathsofar |
| # If it's a complete match, look for gPath$name amongst child |
| # NOTE: may be called directly with pathsofar=NULL |
| if (fullPathMatch(pathsofar, gPath, strict, grep)) { |
| # NOTE: child has to be a gTree if we hope to find a child in it! |
| if (inherits(child, "gTree") && |
| nameMatch(gPath$name, childName, grep[depth(gPath)])) { |
| newchild <- removeFromGTree(child, name, grepname) |
| if (!is.null(newchild)) { |
| gTree$children[[childName]] <- newchild |
| found <- TRUE |
| } |
| } |
| # Otherwise recurse down child |
| } else { |
| # NOTE: may be called directly with pathsofar=NULL |
| if (is.null(pathsofar)) |
| newpathsofar <- child$name |
| else |
| newpathsofar <- paste0(pathsofar, .grid.pathSep, childName) |
| if (!is.null(newChild <- removeGrobFromGPath(child, name, |
| newpathsofar, |
| gPath, strict, |
| grep, grepname, |
| global, warn))) { |
| gTree$children[[childName]] <- newChild |
| found <- TRUE |
| } |
| } |
| } |
| index <- index + 1 |
| } |
| if (found) |
| gTree |
| else |
| NULL |
| } else { |
| NULL |
| } |
| } |
| |
| removeGrobFromGPath.gTree <- function(grob, name, |
| pathsofar, gPath, strict, |
| grep, grepname, global, warn) { |
| if (depth(gPath) == 1) { |
| if (nameMatch(gPath$name, grob$name, grep)) |
| removeFromGTree(grob, name, grepname) |
| else |
| if (strict) |
| NULL |
| else |
| removeGTree(grob, name, |
| if (is.null(pathsofar)) grob$name else pathsofar, |
| gPath, strict, grep, grepname, global, warn) |
| } else { |
| removeGTree(grob, name, |
| if (is.null(pathsofar)) grob$name else pathsofar, |
| gPath, strict, grep, grepname, global, warn) |
| } |
| } |
| |
| removeDLFromGPath <- function(gPath, name, strict, grep, grepname, global, |
| warn, redraw) { |
| dl.index <- grid.Call(C_getDLindex) |
| index <- 1 |
| grob <- NULL |
| found <- FALSE |
| while (index < dl.index && |
| (is.null(grob) || global)) { |
| grob <- removeGrobFromGPath(grid.Call(C_getDLelt, as.integer(index)), |
| name, |
| NULL, gPath, strict, grep, grepname, |
| global, warn) |
| if (!is.null(grob)) { |
| # Destructively modify the DL elt |
| grid.Call(C_setDLindex, as.integer(index)) |
| grid.Call(C_setDLelt, grob) |
| # Reset the DL index |
| grid.Call(C_setDLindex, as.integer(dl.index)) |
| found <- TRUE |
| } |
| index <- index + 1 |
| } |
| if (!found) |
| stop(gettextf("gPath (%s) not found", |
| paste(gPath, name, sep=.grid.pathSep)), |
| domain = NA) |
| else if (redraw) |
| draw.all() |
| } |
| |
| ##### |
| ##### Remove NAME support |
| ##### |
| |
| # NEVER called when strict=TRUE |
| removeGrobFromName <- function(grob, name, grep, global, warn) { |
| UseMethod("removeGrobFromName") |
| } |
| |
| removeGrobFromName.grob <- function(grob, name, grep, global, warn) { |
| NULL |
| } |
| |
| # For a gTree, just recurse straight back to removeName |
| removeGrobFromName.gTree <- function(grob, name, grep, global, warn) { |
| removeName(grob, name, FALSE, grep, global, warn) |
| } |
| |
| removeName <- function(gTree, name, strict, grep, global, warn) { |
| found <- FALSE |
| index <- 1 |
| # Search children for match |
| while (index <= length(gTree$childrenOrder) && |
| (!found || global)) { |
| childName <- gTree$childrenOrder[index] |
| child <- gTree$children[[childName]] |
| # Just check child name and recurse if no match |
| if (nameMatch(name, childName, grep)) { |
| # name might be a regexp, so get real name |
| gTree$children[[gTree$childrenOrder[index]]] <- NULL |
| gTree$childrenOrder <- gTree$childrenOrder[-index] |
| found <- TRUE |
| # If deleted the child, do NOT increase index! |
| } else if (strict) { |
| NULL |
| index <- index + 1 |
| } else { |
| if (!is.null(newChild <- removeGrobFromName(child, name, |
| grep, global, warn))) { |
| gTree$children[[childName]] <- newChild |
| found <- TRUE |
| } |
| index <- index + 1 |
| } |
| } |
| if (found) |
| gTree |
| else |
| NULL |
| } |
| |
| removeNameFromDL <- function(name, strict, grep, global, warn, redraw) { |
| dl.index <- grid.Call(C_getDLindex) |
| index <- 1 |
| grob <- NULL |
| found <- FALSE |
| while (index < dl.index && |
| (is.null(grob) || global)) { |
| grob <- grid.Call(C_getDLelt, as.integer(index)) |
| if (inherits(grob, "grob")) { |
| # If match top-level grob, remove it from DL |
| if (nameMatch(name, grob$name, grep)) { |
| # Destructively modify the DL elt |
| grid.Call(C_setDLindex, as.integer(index)) |
| grid.Call(C_setDLelt, NULL) |
| # Reset the DL index |
| grid.Call(C_setDLindex, as.integer(dl.index)) |
| found <- TRUE |
| # Otherwise search down it for match |
| } else { |
| if (!strict) { |
| grob <- removeGrobFromName(grob, name, grep, global, warn) |
| if (!is.null(grob)) { |
| # Destructively modify the DL elt |
| grid.Call(C_setDLindex, as.integer(index)) |
| grid.Call(C_setDLelt, grob) |
| # Reset the DL index |
| grid.Call(C_setDLindex, as.integer(dl.index)) |
| found <- TRUE |
| } |
| } |
| } |
| } else { |
| grob <- NULL |
| } |
| index <- index + 1 |
| } |
| if (!found) { |
| if (warn) |
| stop(gettextf("gPath (%s) not found", name), domain = NA) |
| } else if (redraw) |
| draw.all() |
| } |
| |
| ################ |
| # Finding a grob from a grob name |
| ################ |
| findgrob <- function(x, name) { |
| UseMethod("findgrob") |
| } |
| |
| findgrob.default <- function(x, name) { |
| NULL |
| } |
| |
| findgrob.grob <- function(x, name) { |
| if (match(name, x$name, nomatch=0L)) |
| x |
| else |
| NULL |
| } |
| |
| findGrobinDL <- function(name) { |
| dl.index <- grid.Call(C_getDLindex) |
| result <- NULL |
| index <- 1 |
| while (index < dl.index && is.null(result)) { |
| result <- findgrob(grid.Call(C_getDLelt, as.integer(index)), name) |
| index <- index + 1 |
| } |
| if (is.null(result)) |
| stop(gettextf("grob '%s' not found", name), domain = NA) |
| result |
| } |
| |
| findGrobinChildren <- function(name, children) { |
| nc <- length(children) |
| result <- NULL |
| index <- 1 |
| while (index <= nc && is.null(result)) { |
| result <- findgrob(children[[index]], name) |
| index <- index + 1 |
| } |
| if (is.null(result)) |
| stop(gettextf("grob '%s' not found", name), domain = NA) |
| result |
| } |
| |
| ################ |
| # grid.draw |
| ################ |
| # Use generic function "draw" rather than generic function "print" |
| # because want graphics functions to produce graphics output |
| # without having to be evaluated at the command-line AND without having |
| # to necessarily produce a single graphical object as the return value |
| # (i.e., so that simple procedural code can be written just for its |
| # side-effects). |
| # For example, so that the following code will draw |
| # a rectangle AND a line: |
| # temp <- function() { grid.lines(); grid.rect() } |
| # temp() |
| grid.draw <- function(x, recording=TRUE) { |
| # If 'x' is NULL, draw nothing |
| if (!is.null(x)) |
| UseMethod("grid.draw") |
| } |
| |
| grid.draw.viewport <- function(x, recording) { |
| pushViewport(x, recording=FALSE) |
| } |
| |
| grid.draw.vpPath <- function(x, recording) { |
| # Assumes strict=FALSE, BUT in order to get onto |
| # display list it must have worked => strict same as non-strict |
| downViewport(x, recording=FALSE) |
| } |
| |
| grid.draw.pop <- function(x, recording) { |
| popViewport(x, recording=FALSE) |
| } |
| |
| grid.draw.up <- function(x, recording) { |
| upViewport(x, recording=FALSE) |
| } |
| |
| pushgrobvp <- function(vp) { |
| UseMethod("pushgrobvp") |
| } |
| |
| pushgrobvp.viewport <- function(vp) { |
| pushViewport(vp, recording=FALSE) |
| } |
| |
| pushgrobvp.vpPath <- function(vp) { |
| downViewport(vp, strict=TRUE, recording=FALSE) |
| } |
| |
| popgrobvp <- function(vp) { |
| UseMethod("popgrobvp") |
| } |
| |
| popgrobvp.viewport <- function(vp) { |
| # NOTE that the grob's vp may be a vpStack/List/Tree |
| upViewport(depth(vp), recording=FALSE) |
| } |
| |
| popgrobvp.vpPath <- function(vp) { |
| upViewport(depth(vp), recording=FALSE) |
| } |
| |
| preDraw <- function(x) { |
| UseMethod("preDraw") |
| } |
| |
| pushvpgp <- function(x) { |
| if (!is.null(x$vp)) |
| pushgrobvp(x$vp) |
| if (!is.null(x$gp)) { |
| set.gpar(x$gp, engineDL=FALSE) |
| } |
| } |
| |
| makeContext <- function(x) { |
| UseMethod("makeContext") |
| } |
| |
| makeContext.default <- function(x) { |
| x |
| } |
| |
| makeContent <- function(x) { |
| UseMethod("makeContent") |
| } |
| |
| makeContent.default <- function(x) { |
| x |
| } |
| |
| preDraw.grob <- function(x) { |
| # Allow customisation of x$vp |
| x <- makeContext(x) |
| # automatically push/pop the viewport and set/unset the gpar |
| pushvpgp(x) |
| preDrawDetails(x) |
| x |
| } |
| |
| preDraw.gTree <- function(x) { |
| # Allow customisation of x$vp (and x$childrenvp) |
| x <- makeContext(x) |
| # Make this gTree the "current grob" for evaluation of |
| # grobwidth/height units via gPath |
| # Do this as a .Call.graphics to get it onto the base display list |
| grid.Call.graphics(C_setCurrentGrob, x) |
| # automatically push/pop the viewport |
| pushvpgp(x) |
| # Push then "up" childrenvp |
| if (!is.null(x$childrenvp)) { |
| # Save any x$gp gpar settings |
| tempgp <- grid.Call(C_getGPar) |
| pushViewport(x$childrenvp, recording=FALSE) |
| upViewport(depth(x$childrenvp), recording=FALSE) |
| # reset the x$gp gpar settings |
| # The upViewport above may have overwritten them with |
| # the previous vp$gp settings |
| grid.Call.graphics(C_setGPar, tempgp) |
| } |
| preDrawDetails(x) |
| x |
| } |
| |
| postDraw <- function(x) { |
| UseMethod("postDraw") |
| } |
| |
| postDraw.grob <- function(x) { |
| postDrawDetails(x) |
| if (!is.null(x$vp)) |
| popgrobvp(x$vp) |
| } |
| |
| drawGrob <- function(x) { |
| # Temporarily turn off the grid DL so that |
| # nested calls to drawing code do not get recorded |
| dlon <- grid.Call(C_setDLon, FALSE) |
| # If get error or user-interrupt, need to reset state |
| # Need to turn grid DL back on (if it was on) |
| on.exit(grid.Call(C_setDLon, dlon)) |
| # Save current gpar |
| tempgpar <- grid.Call(C_getGPar) |
| # If get error or user-interrupt, need to reset state |
| # Need to restore current grob (gtree predraw sets current grob) |
| # Need to restore gpar settings (set by gtree itself and/or its vp) |
| # This does not need to be a grid.Call.graphics() because |
| # we are nested within a recordGraphics() |
| # Do not call set.gpar because set.gpar accumulates cex |
| on.exit(grid.Call(C_setGPar, tempgpar), add=TRUE) |
| # Setting up the drawing context may involve modifying the grob |
| # (typically only x$vp) but the modified grob is needed for postDraw() |
| x <- preDraw(x) |
| # Allow customisation of x |
| # (should only return a basic grob that has a drawDetails() |
| # method, otherwise nothing will be drawn) |
| x <- makeContent(x) |
| # Do any class-specific drawing |
| drawDetails(x, recording=FALSE) |
| postDraw(x) |
| } |
| |
| grid.draw.grob <- function(x, recording=TRUE) { |
| engineDLon <- grid.Call(C_getEngineDLon) |
| if (engineDLon) |
| recordGraphics(drawGrob(x), |
| list(x=x), |
| getNamespace("grid")) |
| else |
| drawGrob(x) |
| if (recording) |
| record(x) |
| invisible() |
| } |
| |
| drawGList <- function(x) { |
| # DO NOT turn off grid DL. |
| # A top-level gList does not itself go on the DL, |
| # but its children do. |
| # A gList which is part of some other grob (e.g., children |
| # of a gTree) will be "protected" by the gTree |
| # turning off the DL. |
| lapply(x, grid.draw) |
| } |
| |
| grid.draw.gList <- function(x, recording=TRUE) { |
| engineDLon <- grid.Call(C_getEngineDLon) |
| if (engineDLon) |
| recordGraphics(drawGList(x), |
| list(x=x), |
| getNamespace("grid")) |
| else |
| drawGList(x) |
| invisible() |
| } |
| |
| drawGTree <- function(x) { |
| # Temporarily turn off the grid DL so that |
| # nested calls to drawing code do not get recorded |
| dlon <- grid.Call(C_setDLon, FALSE) |
| # If get error or user-interrupt, need to reset state |
| # Need to turn grid DL back on (if it was on) |
| on.exit(grid.Call(C_setDLon, dlon)) |
| # Save current grob and current gpar |
| tempgrob <- grid.Call(C_getCurrentGrob) |
| tempgpar <- grid.Call(C_getGPar) |
| # If get error or user-interrupt, need to reset state |
| # Need to restore current grob (gtree predraw sets current grob) |
| # Need to restore gpar settings (set by gtree itself and/or its vp) |
| # This does not need to be a grid.Call.graphics() because |
| # we are nested within a recordGraphics() |
| # Do not call set.gpar because set.gpar accumulates cex |
| on.exit({ grid.Call(C_setGPar, tempgpar) |
| grid.Call(C_setCurrentGrob, tempgrob) |
| }, add=TRUE) |
| # Setting up the drawing context may involve modifying the grob |
| # (typically only x$vp) but the modified grob is needed for postDraw() |
| x <- preDraw(x) |
| # Allow customisation of x (should be confined to x$children) |
| x <- makeContent(x) |
| # Do any class-specific drawing |
| drawDetails(x, recording=FALSE) |
| # Draw all children IN THE RIGHT ORDER |
| for (i in x$childrenOrder) |
| grid.draw(x$children[[i]], recording=FALSE) |
| postDraw(x) |
| } |
| |
| grid.draw.gTree <- function(x, recording=TRUE) { |
| engineDLon <- grid.Call(C_getEngineDLon) |
| if (engineDLon) |
| recordGraphics(drawGTree(x), |
| list(x=x), |
| getNamespace("grid")) |
| else |
| drawGTree(x) |
| if (recording) |
| record(x) |
| invisible() |
| } |
| |
| draw.all <- function() { |
| grid.newpage(recording=FALSE) |
| dl.index <- grid.Call(C_getDLindex) |
| if (dl.index > 1) |
| # Start at 2 because first element is viewport[ROOT] |
| for (i in 2:dl.index) { |
| grid.draw(grid.Call(C_getDLelt, as.integer(i - 1)), |
| recording=FALSE) |
| } |
| } |
| |
| draw.details <- function(x, recording) { |
| .Defunct("drawDetails") |
| } |
| |
| preDrawDetails <- function(x) { |
| UseMethod("preDrawDetails") |
| } |
| |
| preDrawDetails.grob <- function(x) { |
| } |
| |
| postDrawDetails <- function(x) { |
| UseMethod("postDrawDetails") |
| } |
| |
| postDrawDetails.grob <- function(x) { |
| } |
| |
| drawDetails <- function(x, recording) { |
| UseMethod("drawDetails") |
| } |
| |
| drawDetails.grob <- function(x, recording) { |
| } |
| |
| grid.copy <- function(grob) { |
| warning("this function is redundant and will disappear in future versions", |
| domain = NA) |
| grob |
| } |
| |
| ################################ |
| # Flattening a grob |
| |
| forceGrob <- function(x) { |
| UseMethod("forceGrob") |
| } |
| |
| # The default action is to leave 'x' untouched |
| # BUT it is also necessary to enforce the drawing context |
| # for viewports and vpPaths |
| forceGrob.default <- function(x) { |
| grid.draw(x, recording=FALSE) |
| x |
| } |
| |
| # This allows 'x' to be modified, but may not |
| # change 'x' at all |
| forceGrob.grob <- function(x) { |
| # Copy of the original object to allow a "revert" |
| originalX <- x |
| # Same set up as drawGrob() |
| dlon <- grid.Call(C_setDLon, FALSE) |
| on.exit(grid.Call(C_setDLon, dlon)) |
| tempgpar <- grid.Call(C_getGPar) |
| on.exit(grid.Call(C_setGPar, tempgpar), add=TRUE) |
| # Same drawing context set up as drawGrob() |
| # including enforcing the drawing context |
| x <- preDraw(x) |
| # Same drawing content set up as drawGrob() ... |
| x <- makeContent(x) |
| # BUT NO DRAWING |
| # Same context clean up as drawGrob() |
| postDraw(x) |
| # If 'x' has not changed, just return original 'x' |
| # Also, do not bother with saving original |
| # If 'x' has changed ... |
| if (!identical(x, originalX)) { |
| # Store the original object to allow a "revert" |
| x$.ORIGINAL <- originalX |
| # Return the 'x' that would have been drawn |
| # This will typically be a standard R primitive |
| # (which do not have makeContext() or makeContent() |
| # methods, only drawDetails()) |
| # BUT ot be safe add "forcedgrob" class so that subsequent |
| # draws will NOT run makeContext() or makeContent() |
| # methods |
| class(x) <- c("forcedgrob", class(x)) |
| } |
| x |
| } |
| |
| # This allows 'x' to be modified, but may not |
| # change 'x' at all |
| forceGrob.gTree <- function(x) { |
| # Copy of the original object to allow a "revert" |
| originalX <- x |
| # Same set up as drawGTree() |
| dlon <- grid.Call(C_setDLon, FALSE) |
| on.exit(grid.Call(C_setDLon, dlon)) |
| tempgrob <- grid.Call(C_getCurrentGrob) |
| tempgpar <- grid.Call(C_getGPar) |
| on.exit({ grid.Call(C_setGPar, tempgpar) |
| grid.Call(C_setCurrentGrob, tempgrob) |
| }, add=TRUE) |
| # Same drawing context set up as drawGTree(), |
| # including enforcing the drawing context |
| x <- preDraw(x) |
| # Same drawing content set up as drawGTree() ... |
| x <- makeContent(x) |
| # Ensure that children are also forced |
| x$children <- do.call("gList", lapply(x$children, forceGrob)) |
| # BUT NO DRAWING |
| # Same context clean up as drawGTree() |
| postDraw(x) |
| # If 'x' has changed ... |
| if (!identical(x, originalX)) { |
| # Store the original object to allow a "revert" |
| x$.ORIGINAL <- originalX |
| # Return the 'x' that would have been drawn |
| # This will typically be a vanilla gTree with children to draw |
| # (which will not have makeContext() or makeContent() methods) |
| # BUT to be safe add "forcedgrob" class so that subsequent |
| # draws will NOT run makeContext() or makeContent() |
| # methods |
| class(x) <- c("forcedgrob", class(x)) |
| } |
| x |
| } |
| |
| # A "forcedgrob" does NOT modify context or content at |
| # drawing time |
| makeContext.forcedgrob <- function(x) x |
| |
| makeContent.forcedgrob <- function(x) x |
| |
| grid.force <- function(x, ...) { |
| UseMethod("grid.force") |
| } |
| |
| grid.force.default <- function(x, redraw = FALSE, ...) { |
| if (!missing(x)) |
| stop("Invalid force target") |
| # Must upViewport(0) otherwise you risk running the display |
| # list from something other than the ROOT viewport |
| oldcontext <- upViewport(0, recording=FALSE) |
| dl.index <- grid.Call(C_getDLindex) |
| if (dl.index > 1) { |
| # Start at 2 because first element is viewport[ROOT] |
| for (i in 2:dl.index) { |
| grid.Call(C_setDLindex, as.integer(i - 1)) |
| grid.Call(C_setDLelt, |
| forceGrob(grid.Call(C_getDLelt, as.integer(i - 1)))) |
| } |
| grid.Call(C_setDLindex, dl.index) |
| } |
| if (redraw) { |
| draw.all() |
| } |
| # Try to go back to original context |
| if (length(oldcontext)) { |
| seekViewport(oldcontext, recording=FALSE) |
| } |
| } |
| |
| grid.force.grob <- function(x, draw = FALSE, ...) { |
| fx <- forceGrob(x) |
| if (draw) |
| grid.draw(fx) |
| fx |
| } |
| |
| grid.force.character <- function(x, ...) { |
| grid.force(gPath(x), ...) |
| } |
| |
| grid.force.gPath <- function(x, |
| strict=FALSE, grep=FALSE, global=FALSE, |
| redraw = FALSE, ...) { |
| # Use viewports=TRUE so that get vpPaths in result |
| paths <- grid.grep(x, viewports = TRUE, |
| strict = strict, grep = grep, global = global) |
| f <- function(path, ...) { |
| # Only force grobs or gTrees |
| # (might have vpPaths because we said grid.grep(viewports=TRUE)) |
| if (!inherits(path, "gPath")) return() |
| target <- grid.get(path, strict=TRUE) |
| vpPath <- attr(path, "vpPath") |
| depth <- 0 |
| if (nchar(vpPath)) |
| depth <- downViewport(vpPath, recording=FALSE) |
| forcedgrob <- forceGrob(target, ...) |
| if (depth > 0) |
| upViewport(depth, recording=FALSE) |
| grid.set(path, strict=TRUE, forcedgrob) |
| } |
| if (length(paths)) { |
| # To get the force happening in the correct context ... |
| oldcontext <- upViewport(0, recording=FALSE) |
| if (global) { |
| lapply(paths, f, ...) |
| } else { |
| f(paths, ...) |
| } |
| if (redraw) { |
| draw.all() |
| } |
| # Try to go back to original context |
| if (length(oldcontext)) |
| seekViewport(oldcontext, recording=FALSE) |
| } |
| invisible() |
| } |
| |
| revert <- function(x) { |
| UseMethod("revert") |
| } |
| |
| revert.default <- function(x) { |
| x |
| } |
| |
| # Only need to revert "forcedgrob"s |
| revert.forcedgrob <- function(x) { |
| x$.ORIGINAL |
| } |
| |
| # No need for recursion for gTree because if top-level grob |
| # changed its children then top-level grob will have retained |
| # revert version of its entire self (including children) |
| |
| # NOTE that things will get much trickier if allow |
| # grid.revert(gPath = ...) |
| grid.revert <- function(x, ...) { |
| UseMethod("grid.revert") |
| } |
| |
| grid.revert.default <- function(x, redraw=FALSE, ...) { |
| if (!missing(x)) |
| stop("Invalid revert target") |
| dl.index <- grid.Call(C_getDLindex) |
| if (dl.index > 1) { |
| # Start at 2 because first element is viewport[ROOT] |
| for (i in 2:dl.index) { |
| grid.Call(C_setDLindex, as.integer(i - 1)) |
| grid.Call(C_setDLelt, |
| revert(grid.Call(C_getDLelt, as.integer(i - 1)))) |
| } |
| grid.Call(C_setDLindex, dl.index) |
| } |
| if (redraw) { |
| draw.all() |
| } |
| } |
| |
| grid.revert.grob <- function(x, draw=FALSE, ...) { |
| rx <- revert(x) |
| if (draw) { |
| grid.draw(x) |
| } |
| rx |
| } |
| |
| grid.revert.character <- function(x, ...) { |
| grid.revert(gPath(x), ...) |
| } |
| |
| grid.revert.gPath <- function(x, |
| strict=FALSE, grep=FALSE, global=FALSE, |
| redraw = FALSE, ...) { |
| paths <- grid.grep(x, strict = strict, grep = grep, global = global) |
| f <- function(path, ...) { |
| grid.set(path, strict=TRUE, |
| revert(grid.get(path, strict=TRUE), ...)) |
| } |
| if (length(paths)) { |
| if (global) { |
| lapply(paths, f, ...) |
| } else { |
| f(paths, ...) |
| } |
| if (redraw) { |
| draw.all() |
| } |
| } |
| invisible() |
| } |
| |
| ############################### |
| # Reordering grobs |
| |
| # Reorder the children of a gTree |
| # Order may be specified as a character vector |
| # Character vector MUST name existing children |
| # Order may be specified as a numeric vector |
| # (which makes it easy to say something like |
| # "make last child the first child") |
| # Numeric vector MUST be within range 1:numChildren |
| # Only unique order values used |
| # Any children NOT specified by order are appended to |
| # front or back of order (depending on 'front' argument) |
| # Order is ALWAYS back-to-front |
| reorderGrob <- function(x, order, back=TRUE) { |
| if (!inherits(x, "gTree")) |
| stop("can only reorder 'children' for a \"gTree\"") |
| order <- unique(order) |
| oldOrder <- x$childrenOrder |
| N <- length(oldOrder) |
| if (is.character(order)) { |
| # Convert to numeric |
| order <- match(order, x$childrenOrder) |
| } |
| if (is.numeric(order)) { |
| if (any(!is.finite(order)) || |
| !(all(order %in% 1:N))) { |
| stop("Invalid 'order'") |
| } |
| if (back) { |
| newOrder <- c(x$childrenOrder[order], |
| x$childrenOrder[-order]) |
| } else { |
| newOrder <- c(x$childrenOrder[-order], |
| x$childrenOrder[order]) |
| } |
| } |
| x$childrenOrder <- newOrder |
| x |
| } |
| |
| # Reorder the children of a gTree on the display list |
| # (identified by a gPath) |
| # NOTE that it is possible for this operation to produce a grob |
| # that no longer draws (because it relies on another grob that |
| # used to be drawn before it, e.g., when the width of grob "b" |
| # is calculated from the width of grob "a") |
| # Do NOT allow reordering of grobs on the display list |
| # (it is not even clear what should happen in terms of reordering |
| # grobs mixed with viewports PLUS the potential for ending up with |
| # something that will not draw is pretty high) |
| # IF you want to reorder the grobs on the DL, do a grid.grab() |
| # first and then reorder the children of the resulting gTree |
| grid.reorder <- function(gPath, order, back=TRUE, grep=FALSE, redraw=TRUE) { |
| grob <- grid.get(gPath, grep=grep) |
| grid.set(gPath, reorderGrob(grob, order, back=back), |
| grep=grep, redraw=redraw) |
| } |
| |