</TD><TD>G</TD><TD><A HREF=#k:bool>bool</A>
</TD><TD ALIGN="CENTER">false</TD><TD></TD><TD></TD> </TR>
<TR><TD><A NAME=a:layer HREF=#d:layer>layer</A>
-</TD><TD>EN</TD><TD><A HREF=#k:layerRange>layerRange</A>
+</TD><TD>ENC</TD><TD><A HREF=#k:layerRange>layerRange</A>
</TD><TD ALIGN="CENTER">""</TD><TD></TD><TD></TD> </TR>
+ <TR><TD><A NAME=a:layerlistsep HREF=#d:layerlistsep>layerlistsep</A>
+</TD><TD>G</TD><TD>string</TD><TD ALIGN="CENTER">","</TD><TD></TD><TD></TD> </TR>
<TR><TD><A NAME=a:layers HREF=#d:layers>layers</A>
</TD><TD>G</TD><TD><A HREF=#k:layerList>layerList</A>
+</TD><TD ALIGN="CENTER">""</TD><TD></TD><TD></TD> </TR>
+ <TR><TD><A NAME=a:layerselect HREF=#d:layerselect>layerselect</A>
+</TD><TD>G</TD><TD><A HREF=#k:layerRange>layerRange</A>
</TD><TD ALIGN="CENTER">""</TD><TD></TD><TD></TD> </TR>
<TR><TD><A NAME=a:layersep HREF=#d:layersep>layersep</A>
</TD><TD>G</TD><TD>string</TD><TD ALIGN="CENTER">" :\t"</TD><TD></TD><TD></TD> </TR>
<A HREF=#d:orientation><TT>orientation=landscape</TT></A>.
<DT><A NAME=d:layer HREF=#a:layer><STRONG>layer</STRONG></A>
-<DD> Specifies layers in which the node or edge is present.
+<DD> Specifies layers in which the node, edge or cluster is present.
+
+<DT><A NAME=d:layerlistsep HREF=#a:layerlistsep><STRONG>layerlistsep</STRONG></A>
+<DD> Specifies the separator characters used to split the
+ an attribute of type <A HREF=#k:layerRange>layerRange</A> into a list of ranges.
<DT><A NAME=d:layers HREF=#a:layers><STRONG>layers</STRONG></A>
<DD> Specifies a linearly ordered list of layer names attached to the graph
belonging to the current output layer appear. For more information,
see the page <A HREF="http://www.graphviz.org/wiki/how-use-drawing-layers-overlays">How to use drawing layers (overlays)</A>.
+<DT><A NAME=d:layerselect HREF=#a:layerselect><STRONG>layerselect</STRONG></A>
+<DD> Selects a list of layers to be emitted.
+
<DT><A NAME=d:layersep HREF=#a:layersep><STRONG>layersep</STRONG></A>
<DD> Specifies the separator characters used to split the
- <A HREF=#d:layers>layers </A>attribute into a list of layer names.
+ <A HREF=#d:layers>layers</A>attribute into a list of layer names.
<DT><A NAME=d:layout HREF=#a:layout><STRONG>layout</STRONG></A>
<DD> Specifies the name of the layout algorithm to use, such as "dot"
names and implicitly numbered 1,2,...
<DT><A NAME=k:layerRange><STRONG>layerRange</STRONG></A>
-<DD>layerId or layerId<B>s</B>layerId,<BR> where layerId = "all",
+<DD>specifies a list of layers defined by the <A HREF=#a:layers>layers</A> attribute.
+ It consists of a list of layer intervals separated by any collection of characters from
+ the <A HREF=#a:layerlistsep>layerlistsep</A> attribute.
+ Each layer interval is specified as either a
+ layerId or a layerId<B>s</B>layerId<BR>, where layerId = "all",
a decimal integer or a <A HREF=#a:layer>layer</A> name.
- (An integer i corresponds to layer i.)
+ (An integer i corresponds to layer i, layers being numbered from 1.)
The string <B>s</B> consists of 1 or more separator characters specified
by the <A HREF=#a:layersep>layersep</A> attribute.
+ <P>
+ Thus, assuming the default values for <A HREF=#a:layersep>layersep</A>
+ and <A HREF=#a:layerlistsep>layerlistsep</A>, if <TT>layers="a:b:c:d:e:f:g:h"</TT>, the
+ layerRange string <TT>layers="a:b,d,f:all"</TT> would denote the layers
+ <TT>a b d f g h"</TT>.
<DT><A NAME=k:lblString><STRONG>lblString</STRONG></A>
<DD>an <A HREF=#k:escString>escString</A>
}
}
+static boolean is_natural_number(char *sstr)
+{
+ unsigned char *str = (unsigned char *) sstr;
+
+ while (*str)
+ if (NOT(isdigit(*str++)))
+ return FALSE;
+ return TRUE;
+}
+
+static int layer_index(GVC_t *gvc, char *str, int all)
+{
+ /* GVJ_t *job = gvc->job; */
+ int i;
+
+ if (streq(str, "all"))
+ return all;
+ if (is_natural_number(str))
+ return atoi(str);
+ if (gvc->layerIDs)
+ for (i = 1; i <= gvc->numLayers; i++)
+ if (streq(str, gvc->layerIDs[i]))
+ return i;
+ return -1;
+}
+
+static boolean selectedLayer(GVC_t *gvc, int layerNum, int numLayers, char *spec)
+{
+ int n0, n1;
+ unsigned char buf[SMALLBUF];
+ char *w0, *w1;
+ char *buf_part_p = NULL, *buf_p = NULL, *cur, *part_in_p;
+ agxbuf xb;
+ boolean rval = FALSE;
+
+ agxbinit(&xb, SMALLBUF, buf);
+ agxbput(&xb, spec);
+ part_in_p = agxbuse(&xb);
+
+ while ((rval == FALSE) && (cur = strtok_r(part_in_p, gvc->layerListDelims, &buf_part_p))) {
+ w1 = w0 = strtok_r (cur, gvc->layerDelims, &buf_p);
+ if (w0)
+ w1 = strtok_r (NULL, gvc->layerDelims, &buf_p);
+ switch ((w0 != NULL) + (w1 != NULL)) {
+ case 0:
+ rval = FALSE;
+ break;
+ case 1:
+ n0 = layer_index(gvc, w0, layerNum);
+ rval = (n0 == layerNum);
+ break;
+ case 2:
+ n0 = layer_index(gvc, w0, 0);
+ n1 = layer_index(gvc, w1, numLayers);
+ if ((n0 >= 0) || (n1 >= 0)) {
+ if (n0 > n1) {
+ int t = n0;
+ n0 = n1;
+ n1 = t;
+ }
+ rval = BETWEEN(n0, layerNum, n1);
+ }
+ break;
+ }
+ part_in_p = NULL;
+ }
+ agxbfree(&xb);
+ return rval;
+}
+
+static boolean selectedlayer(GVJ_t *job, char *spec)
+{
+ return selectedLayer (job->gvc, job->layerNum, job->numLayers, spec);
+}
+
+/* parse_layerselect:
+ * Parse the graph's layerselect attribute, which determines
+ * which layers are emitted. The specification is the same used
+ * by the layer attribute.
+ *
+ * If we find n layers, we return an array arr of n+2 ints. arr[0]=n.
+ * arr[n+1]=numLayers+1, acting as a sentinel. The other entries give
+ * the desired layer indices.
+ *
+ * If no layers are detected, NULL is returned.
+ *
+ * This implementation does a linear walk through each layer index and
+ * uses selectedLayer to match it against p. There is probably a more
+ * efficient way to do this, but this is simple and until we find people
+ * using huge numbers of layers, it should be adequate.
+ */
+static int* parse_layerselect(GVC_t *gvc, graph_t * g, char *p)
+{
+ int* laylist = N_GNEW(gvc->numLayers+2,int);
+ int i, cnt = 0;
+ for (i = 1; i <=gvc->numLayers; i++) {
+ if (selectedLayer (gvc, i, gvc->numLayers, p)) {
+ laylist[++cnt] = i;
+ }
+ }
+ if (cnt) {
+ laylist[0] = cnt;
+ laylist[cnt+1] = gvc->numLayers+1;
+ }
+ else {
+ agerr(AGWARN, "The layerselect attribute \"%s\" does not match any layer specifed by the layers attribute - ignored.\n", p);
+ laylist[0] = cnt;
+ free (laylist);
+ laylist = NULL;
+ }
+ return laylist;
+}
/* parse_layers:
* Split input string into tokens, with separators specified by
gvc->layerDelims = agget(g, "layersep");
if (!gvc->layerDelims)
gvc->layerDelims = DEFAULT_LAYERSEP;
+ gvc->layerListDelims = agget(g, "layerlistsep");
+ if (!gvc->layerListDelims)
+ gvc->layerListDelims = DEFAULT_LAYERLISTSEP;
+ if ((tok = strpbrk (gvc->layerDelims, gvc->layerListDelims))) { /* conflict in delimiter strings */
+ agerr(AGWARN, "The character \'%c\' appears in both the layersep and layerlistsep attributes - layerlistsep ignored.\n", *tok);
+ gvc->layerListDelims = "";
+ }
ntok = 0;
sz = 0;
/* free layer strings and pointers from previous graph */
if (gvc->layers) {
- free(gvc->layers);
- gvc->layers = NULL;
- }
+ free(gvc->layers);
+ gvc->layers = NULL;
+ }
if (gvc->layerIDs) {
- free(gvc->layerIDs);
- gvc->layerIDs = NULL;
- }
+ free(gvc->layerIDs);
+ gvc->layerIDs = NULL;
+ }
+ if (gvc->layerlist) {
+ free(gvc->layerlist);
+ gvc->layerlist = NULL;
+ }
if ((str = agget(g, "layers")) != 0) {
gvc->numLayers = parse_layers(gvc, g, str);
+ if (((str = agget(g, "layerselect")) != 0) && *str) {
+ gvc->layerlist = parse_layerselect(gvc, g, str);
+ }
} else {
gvc->layerIDs = NULL;
gvc->numLayers = 1;
}
}
-static void firstlayer(GVJ_t *job)
+/* numPhysicalLayers:
+ * Return number of physical layers to be emitted.
+ */
+static int numPhysicalLayers (GVJ_t *job)
+{
+ if (job->gvc->layerlist) {
+ return job->gvc->layerlist[0];
+ }
+ else
+ return job->numLayers;
+
+}
+
+static void firstlayer(GVJ_t *job, int** listp)
{
job->numLayers = job->gvc->numLayers;
- if ((job->numLayers > 1)
+ if (job->gvc->layerlist) {
+ int *list = job->gvc->layerlist;
+ int cnt = *list++;
+ if ((cnt > 1) && (! (job->flags & GVDEVICE_DOES_LAYERS))) {
+ agerr(AGWARN, "layers not supported in %s output\n",
+ job->output_langname);
+ list[1] = job->numLayers + 1; /* only one layer printed */
+ }
+ job->layerNum = *list++;
+ *listp = list;
+ }
+ else {
+ if ((job->numLayers > 1)
&& (! (job->flags & GVDEVICE_DOES_LAYERS))) {
- agerr(AGWARN, "layers not supported in %s output\n",
+ agerr(AGWARN, "layers not supported in %s output\n",
job->output_langname);
- job->numLayers = 1;
+ job->numLayers = 1;
+ }
+ job->layerNum = 1;
+ *listp = NULL;
}
-
- job->layerNum = 1;
}
static boolean validlayer(GVJ_t *job)
return (job->layerNum <= job->numLayers);
}
-static void nextlayer(GVJ_t *job)
+static void nextlayer(GVJ_t *job, int** listp)
{
- job->layerNum++;
+ int *list = *listp;
+ if (list) {
+ job->layerNum = *list++;
+ *listp = list;
+ }
+ else
+ job->layerNum++;
}
static point pagecode(GVJ_t *job, char c)
#endif
}
-static boolean is_natural_number(char *sstr)
-{
- unsigned char *str = (unsigned char *) sstr;
-
- while (*str)
- if (NOT(isdigit(*str++)))
- return FALSE;
- return TRUE;
-}
-
-static int layer_index(GVC_t *gvc, char *str, int all)
-{
- GVJ_t *job = gvc->job;
- int i;
-
- if (streq(str, "all"))
- return all;
- if (is_natural_number(str))
- return atoi(str);
- if (gvc->layerIDs)
- for (i = 1; i <= job->numLayers; i++)
- if (streq(str, gvc->layerIDs[i]))
- return i;
- return -1;
-}
-
-static boolean selectedlayer(GVJ_t *job, char *spec)
-{
- GVC_t *gvc = job->gvc;
- int n0, n1;
- unsigned char buf[SMALLBUF];
- char *w0, *w1;
- agxbuf xb;
- boolean rval = FALSE;
-
- agxbinit(&xb, SMALLBUF, buf);
- agxbput(&xb, spec);
- w1 = w0 = strtok(agxbuse(&xb), gvc->layerDelims);
- if (w0)
- w1 = strtok(NULL, gvc->layerDelims);
- switch ((w0 != NULL) + (w1 != NULL)) {
- case 0:
- rval = FALSE;
- break;
- case 1:
- n0 = layer_index(gvc, w0, job->layerNum);
- rval = (n0 == job->layerNum);
- break;
- case 2:
- n0 = layer_index(gvc, w0, 0);
- n1 = layer_index(gvc, w1, job->numLayers);
- if ((n0 < 0) || (n1 < 0))
- rval = TRUE;
- else if (n0 > n1) {
- int t = n0;
- n0 = n1;
- n1 = t;
- }
- rval = BETWEEN(n0, job->layerNum, n1);
- break;
- }
- agxbfree(&xb);
- return rval;
-}
-
static boolean node_in_layer(GVJ_t *job, graph_t * g, node_t * n)
{
char *pn, *pe;
emit_map_rect(job, job->clip);
gvrender_begin_anchor(job, obj->url, obj->tooltip, obj->target, obj->id);
}
- if (job->numLayers == 1)
+ if (numPhysicalLayers(job) == 1)
emit_background(job, g);
if (GD_label(g))
emit_label(job, EMIT_GLABEL, GD_label(g));
node_t *n;
char *s;
int flags = job->flags;
+ int* lp;
/* device dpi is now known */
job->scale.x = job->zoom * job->dpi.x / POINTS_PER_INCH;
for (n = agfstnode(g); n; n = agnxtnode(g, n))
ND_state(n) = 0;
/* iterate layers */
- for (firstlayer(job); validlayer(job); nextlayer(job)) {
- if (job->numLayers > 1)
+ for (firstlayer(job,&lp); validlayer(job); nextlayer(job,&lp)) {
+ if (numPhysicalLayers (job) > 1)
gvrender_begin_layer(job);
/* iterate pages */
for (firstpage(job); validpage(job); nextpage(job))
emit_page(job, g);
- if (job->numLayers > 1)
+ if (numPhysicalLayers (job) > 1)
gvrender_end_layer(job);
}
emit_end_graph(job, g);