[rkward-cvs] SF.net SVN: rkward-code:[4535] trunk/rkward/packages/rkwarddev
m-eik at users.sf.net
m-eik at users.sf.net
Fri Feb 15 17:02:25 UTC 2013
Revision: 4535
http://sourceforge.net/p/rkward/code/4535
Author: m-eik
Date: 2013-02-15 17:02:25 +0000 (Fri, 15 Feb 2013)
Log Message:
-----------
rkwarddev: introducing optionset generators
Modified Paths:
--------------
trunk/rkward/packages/rkwarddev/ChangeLog
trunk/rkward/packages/rkwarddev/DESCRIPTION
trunk/rkward/packages/rkwarddev/NAMESPACE
trunk/rkward/packages/rkwarddev/R/rk.XML.logic.R
Added Paths:
-----------
trunk/rkward/packages/rkwarddev/R/rk.XML.optioncolumn.R
trunk/rkward/packages/rkwarddev/R/rk.XML.optiondisplay.R
trunk/rkward/packages/rkwarddev/R/rk.XML.optionset.R
trunk/rkward/packages/rkwarddev/inst/doc/rkwarddev_vignette.tex
trunk/rkward/packages/rkwarddev/man/rk.XML.optioncolumn.Rd
trunk/rkward/packages/rkwarddev/man/rk.XML.optiondisplay.Rd
trunk/rkward/packages/rkwarddev/man/rk.XML.optionset.Rd
Modified: trunk/rkward/packages/rkwarddev/ChangeLog
===================================================================
--- trunk/rkward/packages/rkwarddev/ChangeLog 2013-02-15 13:52:55 UTC (rev 4534)
+++ trunk/rkward/packages/rkwarddev/ChangeLog 2013-02-15 17:02:25 UTC (rev 4535)
@@ -10,6 +10,8 @@
- rk.XML.pluginmap() will now move <dependencies> from <about> to a top level child node
of plugin maps, with a warning
added:
+ - new function rk.XML.optiondisplay()
+ - new function rk.XML.optionset()
- new function rk.XML.matrix()
- new function rk.XML.dependencies()
- new function rk.XML.dependency_check()
Modified: trunk/rkward/packages/rkwarddev/DESCRIPTION
===================================================================
--- trunk/rkward/packages/rkwarddev/DESCRIPTION 2013-02-15 13:52:55 UTC (rev 4534)
+++ trunk/rkward/packages/rkwarddev/DESCRIPTION 2013-02-15 17:02:25 UTC (rev 4535)
@@ -85,6 +85,9 @@
'rk.XML.logic.R'
'rk.XML.matrix.R'
'rk.XML.menu.R'
+ 'rk.XML.optioncolumn.R'
+ 'rk.XML.optiondisplay.R'
+ 'rk.XML.optionset.R'
'rk.XML.page.R'
'rk.XML.pluginmap.R'
'rk.XML.preview.R'
Modified: trunk/rkward/packages/rkwarddev/NAMESPACE
===================================================================
--- trunk/rkward/packages/rkwarddev/NAMESPACE 2013-02-15 13:52:55 UTC (rev 4534)
+++ trunk/rkward/packages/rkwarddev/NAMESPACE 2013-02-15 17:02:25 UTC (rev 4535)
@@ -65,6 +65,9 @@
export(rk.XML.logic)
export(rk.XML.matrix)
export(rk.XML.menu)
+export(rk.XML.optioncolumn)
+export(rk.XML.optiondisplay)
+export(rk.XML.optionset)
export(rk.XML.page)
export(rk.XML.plugin)
export(rk.XML.pluginmap)
Modified: trunk/rkward/packages/rkwarddev/R/rk.XML.logic.R
===================================================================
--- trunk/rkward/packages/rkwarddev/R/rk.XML.logic.R 2013-02-15 13:52:55 UTC (rev 4534)
+++ trunk/rkward/packages/rkwarddev/R/rk.XML.logic.R 2013-02-15 17:02:25 UTC (rev 4535)
@@ -35,8 +35,8 @@
# transform "!--" comment nodes into "![CDATA[" for scripting logic
nodes <- sapply(child.list(nodes), function(this.node){
- if(identical(slot(this.node, "name"), "!--")){
- slot(this.node, "name") <- "![CDATA["
+ if(identical(XMLName(this.node), "!--")){
+ XMLName(this.node) <- "![CDATA["
this.node <- XMLNode("script", .children=child.list(this.node, empty=FALSE))
} else {}
return(this.node)
Added: trunk/rkward/packages/rkwarddev/R/rk.XML.optioncolumn.R
===================================================================
--- trunk/rkward/packages/rkwarddev/R/rk.XML.optioncolumn.R (rev 0)
+++ trunk/rkward/packages/rkwarddev/R/rk.XML.optioncolumn.R 2013-02-15 17:02:25 UTC (rev 4535)
@@ -0,0 +1,60 @@
+#' Create XML node "optioncolumn" for RKWard plugins
+#'
+#' These nodes are valid only inside \code{<optionset>} nodes.
+#'
+#' @param id.name Character string, a unique ID for this plugin element.
+#' @param label Either logical or a character string. If given, the optioncolumn will be displayed in the \code{<optiondisplay>} in a column by that label.
+#' If set to \code{TRUE} and you provide a XiMpLe node object to \code{connect}, the label will be extracted from that node.
+#' @param external Logical, set to \code{TRUE} if the optioncolumn is controlled from outside the optionset.
+#' @param connect Either a character string (the \code{id} of the property to connect this optioncolumn to), or an object of
+#' class XiMpLe.node (whose \code{id} will be extracted and used). For external \code{<optioncolumn>}s, the corresponding value will
+#' be set to the externally set value. For regular (non-external) \code{<optioncolumn>}s, the corresponding row of the \code{<optioncolumn>} property, will be set
+#' when the property changes inside the content-area.
+#' @param property Character string, the actual property to connect to, will be appended to the \code{id} of \code{connect}.
+#' @param default Character string, only for external columns: The value to assume for this column, if no value is known for an entry. Rarely useful.
+#' @return An object of class \code{XiMpLe.node}.
+#' @export
+#' @examples
+#' myInput <- rk.XML.input(label="Given name(s)", size="small")
+#' myOptCol <- rk.XML.optioncolumn("os_name", connect=myInput, property="text")
+rk.XML.optioncolumn <- function(id.name, label=TRUE, external=FALSE, connect=NULL, property=NULL, default=NULL){
+
+ attr.list <- list(id=id.name)
+
+ if(is.logical(label)){
+ if(isTRUE(label)){
+ if(is.XiMpLe.node(connect)){
+ attr.list[["label"]] <- XMLAttrs(connect)[["label"]]
+ } else {}
+ } else {
+ }
+ } else if(is.character(label)){
+ attr.list[["label"]] <- label
+ } else {}
+
+ if(isTRUE(external)){
+ attr.list[["external"]] <- "true"
+ } else {}
+
+ if(!is.null(connect)){
+ # let's see if we need to extract IDs first
+ connect.id <- check.ID(connect)
+ # if connect is an XML node, append
+ if(is.XiMpLe.node(connect) && !is.null(property)){
+ # validate get modifier
+ if(modif.validity(connect, modifier=property)){
+ connect.id <- paste(connect.id, property, sep=".")
+ } else {}
+ } else {}
+ attr.list[["connect"]] <- as.character(connect.id)
+ } else {}
+
+ if(!is.null(default)){
+ attr.list[["default"]] <- default
+ } else {}
+
+ node <- XMLNode("optioncolumn",
+ attrs=attr.list)
+
+ return(node)
+}
Added: trunk/rkward/packages/rkwarddev/R/rk.XML.optiondisplay.R
===================================================================
--- trunk/rkward/packages/rkwarddev/R/rk.XML.optiondisplay.R (rev 0)
+++ trunk/rkward/packages/rkwarddev/R/rk.XML.optiondisplay.R 2013-02-15 17:02:25 UTC (rev 4535)
@@ -0,0 +1,25 @@
+#' Create XML node "optiondisplay" for RKWard plugins
+#'
+#' This node is only allowed once insinde the \code{<content>} node of an \code{<optionset>}.
+#'
+#' @param index Logical, whether to show a column with a numeric index in the optiondisplay.
+#' @param id.name Character string, a unique ID for this plugin element (optional).
+#' @return An object of class \code{XiMpLe.node}.
+#' @export
+rk.XML.optiondisplay <- function(index=TRUE, id.name=NULL){
+
+ if(is.null(id.name)){
+ attr.list <- list()
+ } else {
+ attr.list <- list(id=id.name)
+ }
+
+ if(!isTRUE(index)){
+ attr.list[["index"]] <- "false"
+ } else {}
+
+ node <- XMLNode("optiondisplay",
+ attrs=attr.list)
+
+ return(node)
+}
Added: trunk/rkward/packages/rkwarddev/R/rk.XML.optionset.R
===================================================================
--- trunk/rkward/packages/rkwarddev/R/rk.XML.optionset.R (rev 0)
+++ trunk/rkward/packages/rkwarddev/R/rk.XML.optionset.R 2013-02-15 17:02:25 UTC (rev 4535)
@@ -0,0 +1,80 @@
+#' Create XML node "optionset" for RKWard plugins
+#'
+#' @param content A list of XiMpLe.nodes to be placed inside the <content> node of this <optionset>.
+#' @param optioncolumn A list of \code{<optioncolumn>} XiMpLe.nodes.
+#' @param min_rows Numeric (integer), if specified, the set will be marked invalid, unless it has
+#' at least this number of rows. Ignored if set to 0.
+#' @param min_rows_if_any Numeric (integer), like min_rows, but will only be tested, if there is at
+#' least one row. Ignored if set to 0.
+#' @param max_rows Numeric (integer), if specified, the set will be marked invalid, unless it has
+#' at most this number of rows. Ignored if set to 0.
+#' @param keycolumn Character
+#' @param logic A valid \code{<logic>} node.
+#' @param optiondisplay Logical value, can be used to automatically add an \code{<optiondisplay>} node on top
+#' of the \code{<content>} section, if set to something other than \code{NULL}. Depending on whether it's
+#' \code{TRUE} or \code{FALSE}, its \code{index} argument will be set to \code{"true"} or
+#' \code{"false"}, respectively.
+#' @param id.name Character string, a unique ID for this plugin element.
+#' If \code{"auto"}, an ID will be generated automatically from the <content> nodes.
+#' @return An object of class \code{XiMpLe.node}.
+#' @export
+rk.XML.optionset <- function(content, optioncolumn, min_rows=0, min_rows_if_any=0, max_rows=0, keycolumn=NULL, logic=NULL, optiondisplay=NULL, id.name="auto"){
+
+ if(identical(id.name, "auto")){
+ # try autogenerating some id
+ attr.list <- list(id=auto.ids(node.soup(content), prefix=ID.prefix("optionset"), chars=10))
+ } else if(is.null(id.name)){
+ attr.list <- list()
+ } else {
+ attr.list <- list(id=id.name)
+ }
+
+ if(min_rows > 0){
+ attr.list[["min_rows"]] <- min_rows
+ } else {}
+ if(min_rows_if_any > 0){
+ attr.list[["min_rows_if_any"]] <- min_rows_if_any
+ } else {}
+ if(max_rows > 0){
+ attr.list[["max_rows"]] <- max_rows
+ } else {}
+
+ ## TODO: do some checking -- and add support to supply XiMpLe nodes
+ if(!is.null(keycolumn)){
+ attr.list[["keycolumn"]] <- keycolumn
+ } else {}
+
+ if(!is.null(logic)){
+ if(is.XiMpLe.node(logic) && identical(XMLName(logic), "logic")){
+ valid.child("logic", children=XMLChildren(logic))
+ } else {
+ stop(simpleError("'logic' must be a <logic> node!"))
+ }
+ # checks go here
+ } else {}
+
+ # this list will carry all child nodes of the full set
+ all.children <- list(logic)
+
+ content <- child.list(content)
+ optioncolumn <- child.list(optioncolumn)
+
+ # auto-add optiondisplay
+ if(!is.null(optiondisplay)){
+ content <- append(content, rk.XML.optiondisplay(index=optiondisplay), after=0)
+ } else {}
+
+ content.node <- XMLNode("content",
+ .children=content)
+
+ # append content node
+ all.children <- append(all.children, content.node)
+ # append optioncolumns
+ all.children <- append(all.children, optioncolumn)
+
+ node <- XMLNode("optionset",
+ attrs=attr.list,
+ .children=all.children)
+
+ return(node)
+}
Added: trunk/rkward/packages/rkwarddev/inst/doc/rkwarddev_vignette.tex
===================================================================
--- trunk/rkward/packages/rkwarddev/inst/doc/rkwarddev_vignette.tex (rev 0)
+++ trunk/rkward/packages/rkwarddev/inst/doc/rkwarddev_vignette.tex 2013-02-15 17:02:25 UTC (rev 4535)
@@ -0,0 +1,513 @@
+\documentclass[a4paper,10pt]{scrartcl}
+\usepackage[utf8x]{inputenc}
+\usepackage{apacite}
+
+%opening
+\title{RKWard plugin development with the \texttt{rkwarddev} package}
+%\VignetteIndexEntry{RKWard plugin development with the rkwarddev package}
+\author{m.eik michalke}
+
+\usepackage{Sweave}
+\begin{document}
+
+\maketitle
+
+\begin{abstract}
+Writing plugins for \texttt{RKWard} means writing at least two XML files (a GUI description and a plugin map),
+one JavaScript file (to create the \texttt{R} code), maybe a help file (again in XML), and for plugins who
+should be distributed, a DESCRIPTION file. Furtermore, all of these files need to be in a certain directory
+structure.
+
+The \texttt{rkwarddev} package aims to simplify this, by enabling you to fulfill all the listed tasks in just
+one \texttt{R} script.
+\end{abstract}
+
+\section{About the package}
+You might ask why you should write R scripts to generate plugins, if you could just directly write the XML
+and JavaScript files. First of all, you don't have to use this package at all, it's totally fine to code your
+plugins how ever you like. The main reason why I wrote this package is that I like to really concentrate on
+what I'm doing, so this is my attempt to avoid the need to switch between several files in three different languages all the
+time. I wanted to be able to constantly ''think in \texttt{R}`` while working on a plugin, and to oversee everything
+that matters in one script. As a side effect, a lot of useful automation was implemented, so using this package
+will definitely save you quite some amount of typing.
+
+\section{Before we start}
+It is important to undertsand that while \texttt{rkwarddev} can help you to make designing new plugins
+much easier, you still need to know how the generated XML and JavaScript files work and interact. That is, if
+you didn't yet read the \textit{Introduction to Writing Plugins for
+RKWard},\footnote{\url{http://rkward.sourceforge.net/documents/devel/plugins/index.html}} please do so before
+you start working with this package. Once you're sure you understand how plugins in \texttt{RKWard} actually
+work, just come back here.
+
+\section{Ingredients}
+If you look at the contents of the package, you might feel a little lost because of the number of functions.
+So let's first see that there's actually some order in the chaos.
+
+Most functions start with the prefix \texttt{rk.} to indicate that they somehow belong to \texttt{RKWard}
+(we'll get to the exceptions later). After that, many names have another abbreviation by which they can
+roughly be classified into their specific ''area`` of plugin development:
+
+\begin{itemize}
+ \item \texttt{rk.XML.*()}: XML code for GUI description (and plugin maps)
+ \item \texttt{rk.JS.*()}: JavaScript code
+ \item \texttt{rk.rkh.*()}: XML code for help pages
+\end{itemize}
+
+In short, you should find a \texttt{rk.XML.*()} equivalent to every XML tag explained in the
+\textit{Introduction to Writing Plugins for
+RKWard},\footnote{\url{http://rkward.sourceforge.net/documents/devel/plugins/index.html}}
+e.\,g. \texttt{rk.XML.dropdown()} to generate a \texttt{<dropdown>} menu node. There are a few functions for
+JavaScript generation which fall out of this scheme. That is because firstly they should be intuitively to use
+just like their JavaScript equivalent (like \texttt{echo()}), and secondly they are likely to be used very often
+in a script, so short names seemed to be a blessing here (like \texttt{id()} or \texttt{tf()}).
+
+Adding to that, there are some special functions, which will all be explained later, but here's the list,
+roughly ordered by the development stage they're used for:
+
+\begin{itemize}
+ \item \texttt{rk.paste.JS()}: Paste JavaScript code from \texttt{rkwarddev} objects
+ \item \texttt{rk.XML.plugin()}: Combine XML objects into one plugin GUI object
+ \item \texttt{rk.JS.scan()}: Scan a GUI XML file (or \texttt{rkwarddev} object) and generate JavaScript code
+ (define all relevant variables)
+ \item \texttt{rk.JS.saveobj()}: Scan a GUI XML file (or \texttt{rkwarddev} object) and generate JavaScript code
+ (save result objects)
+ \item \texttt{rk.JS.doc()}: Combine JavaScript parts into one plugin JavaScript file object
+ \item \texttt{rk.rkh.scan()}: Scan a GUI XML file (or \texttt{rkwarddev} object) and generate a help page skeleton
+ \item \texttt{rk.rkh.doc()}: Combine XML objects into one help page object
+ \item \texttt{rk.plugin.component()}: Combine XML, JavaScript and help file objects into one plugin component object
+ (i.\,e. one dialog, so \textit{one} plugin can provide \textit{several} dialogs in one package)
+ \item \texttt{rk.testsuite.doc()}: Paste a testsuite skeleton
+ \item \texttt{rk.XML.pluginmap()}: Combine XML objects into one plugin map object
+ \item \texttt{rk.plugin.skeleton()}: Generate actual plugin files from the component,
+ testsuite and plugin map objects (i.\,e., put all of the above together)
+ \item \texttt{rk.build.plugin()}: Compress the generated files into an installable \texttt{R} package for
+ distribution
+\end{itemize}
+
+\subsection{Exceptions to the rule}
+As said before, there are some functions that fall out of the explained name scheme,i.\,e. they don't start with
+\texttt{rk.<XML|JS|rkh>.*()}. They are all relevant for the generation of JavaScript code, and this is just a short overview,
+how you use them will also be explained later on:
+
+\begin{itemize}
+ \item \texttt{echo()}: Produces an equivalent of the JavaScript \texttt{echo()} function
+ \item \texttt{id()}: Similar to paste, but replaces \texttt{rkwarddev} objects with their ID value
+ \item \texttt{ite()}: Short for ''\textbf{i}f, \textbf{t}hen, \textbf{e}lse``, a shortcut to generate JavaScript \texttt{if() \{\} else \{\}} conditions
+ \item \texttt{qp()}: Short for ''\textbf{q}uote \& \textbf{p}lus``, like \texttt{id()}, but with different replacement defaults
+ \item \texttt{tf()}: Short for ''\textbf{t}rue/\textbf{f}alse``, a shortcut to \texttt{ite()} for XML checkbox objects
+ \item \texttt{rk.comment()}: Creates a comment object to show up in the generated code -- works for both XML and JavaScript generation
+\end{itemize}
+
+
+\section{Writing a plugin}
+The previously mentioned \textit{Introduction to Writing Plugins for RKWard}\footnote{\url{http://rkward.sourceforge.net/documents/devel/plugins/index.html}}
+has a chapter on \texttt{rkwarddev} as well, which also includes a full example plugin already. This section will not so much repeat what you can learn there,
+but rather explain the basic steps to create a plugin ''the \texttt{rkwarddev} way`` in general. While doing that, we'll explore some of the alternative options
+you have when using different functions.
+
+Some of them might not be so obvious at first, but I believe that once you know them, you'll learn to like them, too. The basic steps to write a plugin using this
+package can be summarized this way:
+
+\begin{enumerate}
+ \item Know how the \texttt{R} code works you want to generate with the plugin in the end
+ \item Have an idea what the dialog should look like (e.\,g., a varselector left, a varslot and two checkboxes right, etc.)
+ \item Use \texttt{rkwarddev} functions to
+ \begin{enumerate}
+ \item create XML objects for each of these dialog elements individually
+ \item combine these individual objects to one dialog object
+ \item create JavaScript objects (using the XML objects) responsible for the \texttt{R} code of the finished plugin
+ \item create logic, wizard, ... objects the same way
+ \begin{itemize}
+ \item maybe also create help files objects
+ \end{itemize}
+ \item combine all the dialog, logic, wizard, JavaScript ... objects into one plugin and have it written to disk (the plugin map will be generated almost by itself)
+ \end{enumerate}
+\end{enumerate}
+
+So you start with individual parts (the widget elements), combine them, combine what you combined, and so forth.
+
+To begin with some background info, this package makes use of another \texttt{R} package I wrote, called
+\texttt{XiMpLe}\footnote{\url{http://reaktanz.de/?c=hacking\&s=XiMpLe}}. It is a \textit{very} simple XML parser/generator, hence its name.
+All \texttt{rkwarddev} functions dealing with XML utilize tools of this package, that is, the XML objects created are most likely of class
+\texttt{XiMpLe.node}, if not some other \texttt{XiMpLe} class.\footnote{The machanism for JavaScript is basically the same, but those classes and tools
+are all part of \texttt{rkwarddev} itself.}
+
+\subsection{What you see is what you get, in the end}
+Both packages also come with \texttt{show} methods for their objects. This means that the object you create and how it looks when called
+in an R session are not the same: What you will see in your terminal is what the object \textit{would} look like if you \textit{pasted} it
+to a file, using the \texttt{paste} functions of the packages:
+
+ \begin{Schunk}
+ \begin{Sinput}
+> rk.XML.frame(label="Example XML object")
+ \end{Sinput}
+ \begin{Soutput}
+<frame label="Example XML object" id="frm_ExmplXML">
+</frame>
+ \end{Soutput}
+ \end{Schunk}
+
+If you examine the actual structure of the created object with \texttt{str()}, you can see the gory details:
+
+ \begin{Schunk}
+ \begin{Sinput}
+> str(rk.XML.frame(label="Example XML object"))
+ \end{Sinput}
+ \begin{Soutput}
+Formal class 'XiMpLe.node' [package "XiMpLe"] with 4 slots
+ ..@ name : chr "frame"
+ ..@ attributes:List of 2
+ .. ..$ label: chr "Example XML object"
+ .. ..$ id : chr "frm_ExmplXML"
+ ..@ children : list()
+ ..@ value : chr ""
+ \end{Soutput}
+ \end{Schunk}
+
+Most of the time, you won't ever have to worry about that, since the objects will be handled by the package functions automatically.
+But it's important to understand that the results of these functions aren't simple character strings, allthough it might look like it
+at a first glance.
+
+\subsection{Generating XML code}
+In the section before, we have already generated our first XML object: the \texttt{rkwarddev} function \texttt{rk.XML.frame()} produced
+a \texttt{<frame>} node. I guess this is pretty straight forward. Usually, a frame needs some content nodes to make sense, so we'll now
+create two simple checkbox\footnote{As an almost unique exception, the name of \texttt{rk.XML.cbox()} does not match the name of the
+generated XML node, ''checkbox``. Don't worry about that.} objects, put them inside the frame and look at the result:
+
+ \begin{Schunk}
+ \begin{Sinput}
+> myCheckbox <- rk.XML.cbox(label="Check me!")
+> myCheckbox2 <- rk.XML.cbox(label="No, check me!!!", chk=TRUE)
+> myFrame <- rk.XML.frame(myCheckbox, myCheckbox2, label="Example XML object")
+> myFrame
+ \end{Sinput}
+ \begin{Soutput}
+<frame label="Example XML object" id="frm_ExmplXML">
+ <checkbox id="chc_Checkme" label="Check me!" value="true" />
+ <checkbox id="chc_Nocheckm" label="No, check me!!!" value="true" checked="true" />
+</frame>
+ \end{Soutput}
+ \end{Schunk}
+
+What we can see here is that the generated code will automatically be indented, so the result will not only work, but still be human readable
+and look nice (and probably even better than what some might come up with otherwise...). We can also learn how nodes are made nested children
+of other nodes: All \texttt{rkwarddev} functions which can create parent to more than one node have the special ''dots`` parameter in their
+signature (\texttt{...}). That way you can give them arbitrary numbers of XML objects, and they just know what you want them to do with them.
+
+\subsubsection{IDs}
+If you have a closer look you can also see one of the packages' automatic features: The node objects automatically received
+ID values, allthough we didn't specify any. By default, allmost all functions supporting IDs have \texttt{id.name="auto"} set,
+which as we've seen will not cause the ID to become \texttt{"auto"}, but a generated value. Usually an auto-ID is combined
+of the abbreviated node type and the abbreviated label given. So here, our \texttt{<frame>} node labelled ''Example XML object`` got the
+ID \texttt{frm\_ExmplXML}. If we wanted a node to have some specific ID, we can use the \texttt{id.name} argument:
+
+ \begin{Schunk}
+ \begin{Sinput}
+> rk.XML.cbox(label="Check me!", id.name="specificID")
+ \end{Sinput}
+ \begin{Soutput}
+<checkbox id="specificID" label="Check me!" value="true" />
+ \end{Soutput}
+ \end{Schunk}
+
+Now, the fact that these nodes are actually objects of class \texttt{XiMpLe.node} gives us direct access to their attributes, including the ID:
+
+ \begin{Schunk}
+ \begin{Sinput}
+> myCheckbox <- rk.XML.cbox(label="Check me!")
+> myCheckbox at attributes[["id"]]
+ \end{Sinput}
+ \begin{Soutput}
+[1] "chc_Checkme"
+ \end{Soutput}
+ \end{Schunk}
+
+Again, mere mortals probably won't use this directly, but this makes it easy for functions read the IDs of XML nodes and use them. For example,
+if you wanted to define a varselector and a varslot, so the latter can take objects from the former, you need to give the varselector an ID and
+define that as the source in the varslot. If you wrote XML directly, you would give the \texttt{source} attribute the actual ID. With this package,
+you \textit{can} do that too, but there is a much more elegant solution: Give the whole XML object and let the function extract the ID itself:
+
+ \begin{Schunk}
+ \begin{Sinput}
+> (myVarselector <- rk.XML.varselector(id.name="my_vars"))
+ \end{Sinput}
+ \begin{Soutput}
+<varselector id="my_vars" />
+ \end{Soutput}
+ \begin{Sinput}
+> (myVars <- rk.XML.varslot(label="Chose a variable", source=myVarselector))
+ \end{Sinput}
+ \begin{Soutput}
+<varslot id="vrsl_Chosvrbl" label="Chose a variable" source="my_vars" />
+ \end{Soutput}
+ \end{Schunk}
+
+So basically you define an XML object and then re-use this single object throughout your plugin script, be it for actual XML generation or, as
+in this case, only for getting its ID. This means, in other words, you can tell the varslot ''take variables from this object``, you don't have
+to worry about IDs \textit{at all}. Just remember how you named an object and you can do all kinds of things with it.
+
+This context dependent object handling will become even more useful when we get to the JavaScript part.
+
+\subsubsection{From single elements to a dialog}
+Once you have an idea which elements you would like to see in your dialog, and have also created individual XML objects of them, you finally have to
+put them all together to form the full dialog XML. This is done by \texttt{rk.XML.dialog()}, in the same manner that \texttt{rk.XML.frame()} was used.
+To get the layout into the desired structure, use \texttt{rk.XML.row()} and \texttt{rk.XML.col()} to group elements into rows and columns, as nested
+as you see fit:
+
+ \begin{Schunk}
+ \begin{Sinput}
+> (myDialog <- rk.XML.dialog(
++ rk.XML.row(
++ myVarselector,
++ rk.XML.col(myVars, myFrame)),
++ label="Example dialog"))
+ \end{Sinput}
+ \begin{Soutput}
+<dialog label="Example dialog">
+ <row id="row_vCCEXMLEXM">
+ <varselector id="my_vars" />
+ <column id="clm_vCCEXMLEXM">
+ <varslot id="vrsl_Chosvrbl" label="Chose a variable" source="my_vars" />
+ <frame label="Example XML object" id="frm_ExmplXML">
+ <checkbox id="chc_Checkme" label="Check me!" value="true" />
+ <checkbox id="chc_Nocheckm" label="No, check me!!!" value="true" checked="true" />
+ </frame>
+ </column>
+ </row>
+</dialog>
+ \end{Soutput}
+ \end{Schunk}
+
+Now, wouldn't it be nice to see how that looks like in \texttt{RKWard}? Well, you can:
+
+ \begin{Schunk}
+ \begin{Sinput}
+> rk.plugin.skeleton(
++ about="Example plugin",
++ xml=list(dialog=myDialog),
++ load=TRUE,
++ show=TRUE
++ )
+ \end{Sinput}
+ \begin{Soutput}
+For filenames ‘Example plugin’ was renamed to ‘Exampleplugin’.
+Created directory /tmp/Rtmp9gdThb/Exampleplugin.
+Created directory /tmp/Rtmp9gdThb/Exampleplugin/R.
+Created directory /tmp/Rtmp9gdThb/Exampleplugin/inst/rkward/plugins.
+Created directory /tmp/Rtmp9gdThb/Exampleplugin/inst/rkward/tests/Exampleplugin.
+For filenames ‘Example plugin’ was renamed to ‘Exampleplugin’.
+For filenames ‘Example plugin’ was renamed to ‘Exampleplugin’.
+For filenames ‘Example plugin’ was renamed to ‘Exampleplugin’.
+For filenames ‘Example plugin’ was renamed to ‘Exampleplugin’.
+[1] "/tmp/Rtmp9gdThb/Exampleplugin"
+ \end{Soutput}
+ \end{Schunk}
+
+Allthough until now all we did was to outline the XML description of our plugin-to-become, \texttt{rkwarddev}
+can already generate a full plugin. \texttt{load=TRUE} makes sure that \texttt{RKWard} recognizes the new plugin
+immediately after it was created, and \texttt{show=TRUE} will even make it pop up, too:
+
+\begin{center}
+ \includegraphics{./RKWard_vign_example_dialog_wcode.png}
+ % RKWard_vign_example_dialog_wcode.png: 423x407 pixel, 99dpi, 10.85x10.44 cm, bb=0 0 308 296
+\end{center}
+
+Not bad for less than 20 short lines of code. This makes dialog design both very efficient and flexible:
+Imagine you want to re-arrange the order of elements, or experiment with completely different tabbook layouts, all you need to
+do is to change the \texttt{rk.XML.dialog()} call and run \texttt{rk.plugin.skeleton()} again (with \texttt{overwrite=TRUE}).
+If you don't specify a directory explicitly, all will be written to a temporary directory. As seen in the example output,
+the return value of \texttt{rk.plugin.skeleton()} is allways the root directory of the created plugin.
+
+Looking at the attribute \texttt{xml=list(dialog=myDialog)}, we can assume that
+
+\begin{enumerate}
+ \item there's more than a \texttt{dialog} we can provide\\
+ \textit{Further valid options are \texttt{wizard}, \texttt{logic} and \texttt{snippets}}
+ \item there's more to define than just the \texttt{xml} of a plugin
+ \textit{Further arguments include \texttt{js}, \texttt{pluginmap}, \texttt{rkh} and \texttt{components}, among others}
+\end{enumerate}
+
+Of course, this plugin doesn't really do anything useful. In fact, it doesn't matter how you treat the buttons and boxes, the R code below
+won't change a bit, because we didn't provide any JavaScript code to deal with those events.
+
+\subsection{Generating JavaScript code}
+In contrast to what we've seen with the XML code, where objects are nested into others and the result is one big XML object, generated JavaScript code
+is always a plain character string in the end. This is because it doesn't make much sense to treat a programming language otherwise, if you don't want
+to lose its flexibility. But in between, existing objects will be re-used and new ones created as well. The best way to understand how \texttt{rkwarddev}
+handles the JavaScript part is to think of it as a specialized \texttt{paste()}. Its special feature is that it understands the objects we're dealing
+with, and depending on where they occur, knows what strings to make out of them.
+
+Therefore, the most direct approach to get JavaScript into the plugin would to write it all by hand, paste it into a character object and give that to
+\texttt{rk.plugin.skeleton()}. But again, \texttt{rkwarddev} offers some helpful tools.
+
+\subsubsection{Defining variables}
+We just created the XML dialog, which in turn of course includes all the elements (and their IDs) we need to care about. Or the other way round:
+For each element in the dialog it is pretty safe to assume that it should have \textit{some} effect on the outcome. So for a start, \texttt{rkwarddev}
+can ''scan`` the dialog object\footnote{In fact, \texttt{rk.JS.scan()} is not limited to R objects but can also read XML files.},
+collect all relevant IDs and define them as JavaScript variables automatically. Try this for a demonstration:
+
+ \begin{Schunk}
+ \begin{Sinput}
+> cat(rk.JS.scan(myDialog))
+ \end{Sinput}
+ \begin{Soutput}
+ var vrslChosvrbl = getValue("vrsl_Chosvrbl");
+ var chcCheckme = getValue("chc_Checkme");
+ var chcNocheckm = getValue("chc_Nocheckm");
+ \end{Soutput}
+ \end{Schunk}
+
+Notice that only the varslot and both checkboxes show up -- \texttt{rk.JS.scan()} distinguishes between relevant and irrelevant IDs, e.\,g. a
+row or column is no GUI element of interest here. By the way, if you defined the frame as \texttt{checkable=TRUE}, its ID would be
+extracted as well.\footnote{There is also a related function called \texttt{rk.JS.saveobj()}, which scans for \texttt{<saveobject>} nodes and
+does not only define the neccessary variables, but also generates the full JavaScript code snippet for the \texttt{printout()} function,
+to effectively save result objects to workspace.}
+
+You might also notice that the JavaScript variable names differ from the XML IDs. For once, that way they're harder to confuse with each other,
+and there's also some conventions which characters are allowed. But to cut things short, we don't have to worry about the variable names,
+just like we didn't have to care about the XML IDs before. We don't even have to call \texttt{rk.JS.scan()} ourselves, as
+\texttt{rk.plugin.skeleton()} will do so where appropriate, if the option \texttt{scan} includes \texttt{"var"}, which by default is the case.
+
+This means, once more we can concentrate on what the plugin shall actually do, perhaps some calculation of a kind.
+
+\subsubsection{Shortcut functions}
+The common task here is to check for certain dialog events (like unchecking the checkbox labelled ''foo``), and then generate the according
+\texttt{R} code (like \texttt{foo=FALSE}). The question remains, if we don't know the actual variable names, how can we check for events
+of certain dialog elements in the JavaScript code? The answer to that is: We generate the code using \texttt{rkwarddev}'s special
+JavaScripting functions and paste it with \texttt{rk.paste.JS()}. That way, we can use the created XML objects once again, as reference
+to which element we actually mean.
+
+\paragraph{echo()}
+The JavaScript equivalent to \texttt{paste()} is \texttt{echo()}, with a slightly different
+syntax: Concatenation is not done by commas, but by the plus sign, and the line must end with a semicolon. This is an example why it might be
+nice to not need to switch between languages back and forth any more. So \texttt{rkwarddev} has an \texttt{R} function called \texttt{echo()},
+which translates \texttt{paste()}-like arguments into an equivalent JavaScript call:
+
+ \begin{Schunk}
+ \begin{Sinput}
+> echo("# Value of the checkbox is: ", myCheckbox, "\n")
+ \end{Sinput}
+ \begin{Soutput}
+[1] "echo(\"# Value of the checkbox is: \" + chcCheckme + "\n");"
+ \end{Soutput}
+ \end{Schunk}
+
+If this JavaScript code line was used, it would simply add a comment regarding the checkbox value to the \texttt{R} code,
+including a newline.
+
+\paragraph{ite()}
+Now we know how to paste JavaScript code which echoes \texttt{R} code. What we definitely need at some point is \texttt{if()} conditions. For
+that, \texttt{rkwarddev} offers \texttt{ite()}. The function takes up to three arguments: One ''if`` condition, one ''then`` action, and optionally
+one ''else`` action. But actually, neither will the ''if`` condition be evaluated, nor will any of the actions be taken. The arguments just
+define what should be \textit{pasted} at which part if the conditional statement:
+
+ \begin{Schunk}
+ \begin{Sinput}
+> ite("foo", "bar", "baz")
+ \end{Sinput}
+ \begin{Soutput}
+ if(foo) {
+ bar
+ } else {
+ baz
+ }
+ \end{Soutput}
+ \end{Schunk}
+
+However, in contrast to \texttt{echo()}, what \texttt{ite()} returns is not a character string, but similar to what we've seen with the XML
+functions a special JavaScript object. Amongst other things, this is useful to again generate readable code, e.\,g. nested conditions:
+
+ \begin{Schunk}
+ \begin{Sinput}
+> ite(myCheckbox,
++ ite(myVars,
++ echo("result <- ", myVars, "\n"),
++ echo("# huh?\n")
++ ),
++ ite(myCheckbox2,
++ echo("## ouch!\n")
++ )
++ )
+ \end{Sinput}
+ \begin{Soutput}
+ if(chcCheckme) {
+ if(vrslChosvrbl) {
+ echo("result <- " + vrslChosvrbl + "\n");
+ } else {
+ echo("# huh?\n");
+ }
+ } else if(chcNocheckm) {
+ echo("## ouch!\n");
+ } else {}
+ \end{Soutput}
+ \end{Schunk}
+
+To finally use this object in the plugin, it must be evaluated and transformed into a character string.
+
+\paragraph{rk.paste.JS()}
+This is where \texttt{rk.paste.JS()} comes into play:
+
+ \begin{Schunk}
+ \begin{Sinput}
+> myCalculation <- rk.paste.JS(
++ ite(myCheckbox,
++ ite(myVars,
++ echo("result <- ", myVars, "\n"),
++ echo("# huh?\n")
++ ),
++ ite(myCheckbox2,
++ echo("## ouch!\n")
++ )
++ )
++ )
++ rk.plugin.skeleton(
++ about="Example plugin",
++ xml=list(dialog=myDialog),
++ js=list(calculate=myCalculation),
++ load=TRUE,
++ show=TRUE,
++ overwrite=TRUE
++ )
+ \end{Sinput}
+ \begin{Soutput}
+For filenames 'Example plugin' was renamed to 'Exampleplugin'.
+For filenames 'Example plugin' was renamed to 'Exampleplugin'.
+For filenames 'Example plugin' was renamed to 'Exampleplugin'.
+For filenames 'Example plugin' was renamed to 'Exampleplugin'.
+For filenames 'Example plugin' was renamed to 'Exampleplugin'.
+[1] "/tmp/Rtmp9gdThb/Exampleplugin"
+ \end{Soutput}
+ \end{Schunk}
+
+Now the plugin actually changes the generated code if you select an object from the workspace and toggle the checkboxes:
+
+\begin{center}
+ \includegraphics{./RKWard_vign_example_dialog_wcode_JS.png}
+ % RKWard_vign_example_dialog_wcode_JS.png: 763x453 pixel, 99dpi, 19.57x11.62 cm, bb=0 0 555 329
+\end{center}
+
+% \subsection{The whole is more than the sum of its parts}
+%
+%
+%
+% \begin{Schunk}
+% \begin{Sinput}
+% s
+% \end{Sinput}
+% \begin{Soutput}
+% s
+% \end{Soutput}
+% \end{Schunk}
+
+% \begin{Schunk}
+% \begin{Sinput}
+% \end{Sinput}
+% \begin{Soutput}
+% \end{Soutput}
+% \end{Schunk}
+
+% \bibliographystyle{apacite}
+% \addcontentsline{toc}{chapter}{\bibname}
+% \bibliography{rkwarddev_lit}
+
+\end{document}
Added: trunk/rkward/packages/rkwarddev/man/rk.XML.optioncolumn.Rd
===================================================================
--- trunk/rkward/packages/rkwarddev/man/rk.XML.optioncolumn.Rd (rev 0)
+++ trunk/rkward/packages/rkwarddev/man/rk.XML.optioncolumn.Rd 2013-02-15 17:02:25 UTC (rev 4535)
@@ -0,0 +1,52 @@
+\name{rk.XML.optioncolumn}
+\alias{rk.XML.optioncolumn}
+\title{Create XML node "optioncolumn" for RKWard plugins}
+\usage{
+ rk.XML.optioncolumn(id.name, label = TRUE,
+ external = FALSE, connect = NULL, property = NULL,
+ default = NULL)
+}
+\arguments{
+ \item{id.name}{Character string, a unique ID for this
+ plugin element.}
+
+ \item{label}{Either logical or a character string. If
+ given, the optioncolumn will be displayed in the
+ \code{<optiondisplay>} in a column by that label. If set
+ to \code{TRUE} and you provide a XiMpLe node object to
+ \code{connect}, the label will be extracted from that
+ node.}
+
+ \item{external}{Logical, set to \code{TRUE} if the
+ optioncolumn is controlled from outside the optionset.}
+
+ \item{connect}{Either a character string (the \code{id}
+ of the property to connect this optioncolumn to), or an
+ object of class XiMpLe.node (whose \code{id} will be
+ extracted and used). For external \code{<optioncolumn>}s,
+ the corresponding value will be set to the externally set
+ value. For regular (non-external) \code{<optioncolumn>}s,
+ the corresponding row of the \code{<optioncolumn>}
+ property, will be set when the property changes inside
+ the content-area.}
+
+ \item{property}{Character string, the actual property to
+ connect to, will be appended to the \code{id} of
+ \code{connect}.}
+
+ \item{default}{Character string, only for external
+ columns: The value to assume for this column, if no value
+ is known for an entry. Rarely useful.}
+}
+\value{
+ An object of class \code{XiMpLe.node}.
+}
+\description{
+ These nodes are valid only inside \code{<optionset>}
+ nodes.
+}
+\examples{
+myInput <- rk.XML.input(label="Given name(s)", size="small")
+myOptCol <- rk.XML.optioncolumn("os_name", connect=myInput, property="text")
+}
+
Added: trunk/rkward/packages/rkwarddev/man/rk.XML.optiondisplay.Rd
===================================================================
--- trunk/rkward/packages/rkwarddev/man/rk.XML.optiondisplay.Rd (rev 0)
+++ trunk/rkward/packages/rkwarddev/man/rk.XML.optiondisplay.Rd 2013-02-15 17:02:25 UTC (rev 4535)
@@ -0,0 +1,21 @@
+\name{rk.XML.optiondisplay}
+\alias{rk.XML.optiondisplay}
+\title{Create XML node "optiondisplay" for RKWard plugins}
+\usage{
+ rk.XML.optiondisplay(index = TRUE, id.name = NULL)
+}
+\arguments{
+ \item{index}{Logical, whether to show a column with a
+ numeric index in the optiondisplay.}
+
+ \item{id.name}{Character string, a unique ID for this
+ plugin element (optional).}
+}
+\value{
+ An object of class \code{XiMpLe.node}.
+}
+\description{
+ This node is only allowed once insinde the
+ \code{<content>} node of an \code{<optionset>}.
+}
+
Added: trunk/rkward/packages/rkwarddev/man/rk.XML.optionset.Rd
===================================================================
--- trunk/rkward/packages/rkwarddev/man/rk.XML.optionset.Rd (rev 0)
+++ trunk/rkward/packages/rkwarddev/man/rk.XML.optionset.Rd 2013-02-15 17:02:25 UTC (rev 4535)
@@ -0,0 +1,50 @@
+\name{rk.XML.optionset}
+\alias{rk.XML.optionset}
+\title{Create XML node "optionset" for RKWard plugins}
+\usage{
+ rk.XML.optionset(content, optioncolumn, min_rows = 0,
+ min_rows_if_any = 0, max_rows = 0, keycolumn = NULL,
+ logic = NULL, optiondisplay = NULL, id.name = "auto")
+}
+\arguments{
+ \item{content}{A list of XiMpLe.nodes to be placed inside
+ the <content> node of this <optionset>.}
+
+ \item{optioncolumn}{A list of \code{<optioncolumn>}
+ XiMpLe.nodes.}
+
+ \item{min_rows}{Numeric (integer), if specified, the set
+ will be marked invalid, unless it has at least this
+ number of rows. Ignored if set to 0.}
+
+ \item{min_rows_if_any}{Numeric (integer), like min_rows,
+ but will only be tested, if there is at least one row.
+ Ignored if set to 0.}
+
+ \item{max_rows}{Numeric (integer), if specified, the set
+ will be marked invalid, unless it has at most this number
+ of rows. Ignored if set to 0.}
+
+ \item{keycolumn}{Character}
+
+ \item{logic}{A valid \code{<logic>} node.}
+
+ \item{optiondisplay}{Logical value, can be used to
+ automatically add an \code{<optiondisplay>} node on top
+ of the \code{<content>} section, if set to something
+ other than \code{NULL}. Depending on whether it's
+ \code{TRUE} or \code{FALSE}, its \code{index} argument
+ will be set to \code{"true"} or \code{"false"},
+ respectively.}
+
+ \item{id.name}{Character string, a unique ID for this
+ plugin element. If \code{"auto"}, an ID will be
+ generated automatically from the <content> nodes.}
+}
+\value{
+ An object of class \code{XiMpLe.node}.
+}
+\description{
+ Create XML node "optionset" for RKWard plugins
+}
+
More information about the rkward-tracker
mailing list